You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

await切换上下文致Mutex释放异常,单例服务懒加载求解

问题原因

Mutex是线程绑定的同步原语:必须由调用WaitOne()获取锁的线程来调用ReleaseMutex()释放锁。而await关键字会在异步操作完成后,可能调度到不同的线程继续执行后续代码(即使使用ConfigureAwait(true),也无法保证一定回到原线程——比如原线程已被回收、或上下文不存在时,仍会切换到线程池其他线程)。此时finally块中的ReleaseMutex()由非获取锁的线程执行,就会触发上述异常。

替代实现方案

方案1:使用SemaphoreSlim(支持异步等待的同步原语)

SemaphoreSlim不绑定线程,支持异步等待,适合异步场景下的线程安全控制:

// 替换原Mutex成员
private readonly SemaphoreSlim _semaphore = new(1, 1);
private readonly Dictionary<string, List<string>> _fromToAll = [];

private async Task<Dictionary<string, List<string>>> FindFromToAll()
{
    await _semaphore.WaitAsync();
    try
    {
        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
    {
        _semaphore.Release();
    }
}

方案2:使用Lazy<Task<T>>实现异步延迟初始化

利用Lazy<T>的线程安全特性(默认模式下LazyThreadSafetyMode.ExecutionAndPublication,保证初始化只执行一次),结合异步任务完成初始化:

private readonly Lazy<Task<Dictionary<string, List<string>>>> _lazyFromToAll;

// 在服务构造函数中初始化Lazy
public YourSingletonService(IServiceProvider provider)
{
    _lazyFromToAll = new Lazy<Task<Dictionary<string, List<string>>>>(async () =>
    {
        var result = new Dictionary<string, List<string>>();
        foreach (var serviceType in FindAllServiceTypes())
        {
            var service = ActivatorUtilities.CreateInstance(provider, serviceType);
            var fromTo = await service.Invoke<Task<List<string>>>("FromTo");
            result.Add(serviceType.Name.Replace("Service", null), fromTo);
        }
        return result;
    });
}

private Task<Dictionary<string, List<string>>> FindFromToAll()
{
    return _lazyFromToAll.Value;
}

这种方式无需手动管理锁,Lazy<T>会自动处理线程安全,且初始化逻辑仅执行一次。

方案3:服务启动时提前初始化

如果业务允许,可在服务启动阶段完成字典初始化,避免运行时的异步锁问题:

// 在服务构造函数或启动逻辑中执行
public YourSingletonService(IServiceProvider provider)
{
    _fromToAll = InitializeFromToAllAsync(provider).GetAwaiter().GetResult();
}

private async Task<Dictionary<string, List<string>>> InitializeFromToAllAsync(IServiceProvider provider)
{
    var result = new Dictionary<string, List<string>>();
    foreach (var serviceType in FindAllServiceTypes())
    {
        var service = ActivatorUtilities.CreateInstance(provider, serviceType);
        var fromTo = await service.Invoke<Task<List<string>>>("FromTo");
        result.Add(serviceType.Name.Replace("Service", null), fromTo);
    }
    return result;
}

注意:此方式会延长服务启动时间,需根据业务场景评估是否可接受。


内容的提问来源于stack exchange,提问作者Gion Ion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 21:37:51