互斥体释放会抛出异常,因为等待更改上下文

问题描述 投票:0回答:1

我有一个服务,我将其作为单例注入,它具有:

    private readonly Dictionary<string, List<string>> _fromToAll = [];
    private readonly Mutex _mutex = new();

并且我希望字典仅在需要时才延迟初始化一次,例如:

    private async Task<Dictionary<string, List<string>>> FindFromToAll()
    {
        try
        {
            _mutex.WaitOne();
            if (_fromToAll.Count > 0)
            {
                return _fromToAll;
            }

            foreach (var serviceType in FindAllServiceTypes())
            {
                var service = ActivatorUtilities.CreateInstance(provider, serviceType);
                var fromTo = await service.Invoke<Task<List<string>>>("FromTo");
                _fromToAll.Add(serviceType.Name.Replace("Service", null), fromTo);
            }

            return _fromToAll;
        }
        finally
        {
            _mutex.ReleaseMutex();
        }
    }

但是当我调用并等待返回字符串列表的 FromTo 任务时,上下文会发生变化,并且当我释放互斥锁时,它会崩溃,因为我从另一个线程释放它,异常消息如下所示:“调用了对象同步方法来自不同步的代码块。”

知道为什么会发生这种情况或者我该如何做不同的事情吗? 预先感谢您😃

即使我添加ConfigureAwait(true)也不会将上下文切换到捕获的上下文。

c# rest async-await mutex
1个回答
0
投票

互斥体具有线程关联性,这意味着每当您将其锁定在一个线程上时,该线程都必须将其解锁。当您

await
没有同步上下文的东西时,延续会在线程池上进行,因此,运行它的代码(以及解锁)与最初锁定它的代码不同的可能性非常高。

解决方案是使用不同的原语。最简单的方法是使用

SemaphoreSlim
,它是异步感知的,初始计数为 1:

private readonly SemaphoreSlim _sem = new SemaphoreSlim(1);

然后,你的代码变成:

try
{
    await _sem.WaitAsync();
    
    // rest of the code is unchanged
}
finally
{
    _sem.Release();
}

就像

Mutex
一样,
SemaphoreSlim
 是一次性的,因此请确保在使用完毕后将其丢弃。

作为额外奖励,如果您想要可取消的等待,可以将

CancellationToken
传递给
WaitAsync()

© www.soinside.com 2019 - 2024. All rights reserved.