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

Web API令牌并发更新问题及Mutex同步报错解决方案咨询

问题分析与解决方案

错误原因

你遇到的Object synchronization method was called from an unsynchronized block of code错误,核心原因是**Mutex与async/await不兼容**:

  • 调用mutex.WaitOne()的线程会获取Mutex的所有权,但await AccessTokenEndpointCallAsync()会触发线程切换,后续执行finally块的线程已不是最初获取Mutex的线程。
  • Mutex强制要求必须由获取它的线程调用ReleaseMutex(),跨线程释放就会触发该错误。
  • 此外,你的场景是单进程内的并发控制,Mutex作为跨进程同步原语属于过度使用,应该用更轻量、支持异步的同步工具。

正确解决方案:使用SemaphoreSlim实现异步安全的双重检查锁

SemaphoreSlim支持异步等待(WaitAsync()),完美适配async/await场景,同时能实现单进程内的并发控制。修改后的代码如下:

// 在类级别声明SemaphoreSlim实例,初始计数设为1(模拟互斥锁)
private readonly SemaphoreSlim _tokenLock = new SemaphoreSlim(1, 1);

public async Task<string> GetAccessTokenAsync()
{
    // 首次检查令牌状态,避免不必要的锁竞争
    if (!_storage.IsTokenExpired())
    {
        return _storage.ReadAccessToken();
    }

    try
    {
        // 异步等待获取锁,不阻塞线程
        await _tokenLock.WaitAsync();
        
        // 二次检查令牌:防止等待锁期间已有其他线程更新了令牌
        if (!_storage.IsTokenExpired())
        {
            return _storage.ReadAccessToken();
        }

        // 获取新令牌并更新缓存
        var newToken = await AccessTokenEndpointCallAsync();
        _storage.StoreAccessToken(newToken);
        return newToken;
    }
    finally
    {
        // 无论是否发生异常,都释放锁
        _tokenLock.Release();
    }
}

关键优化点

  • 异步安全:SemaphoreSlim.WaitAsync()适配async/await的线程切换特性,释放锁的操作不依赖原线程,不会触发同步错误。
  • 双重检查锁:两次验证令牌状态,既减少了无意义的锁竞争,又避免了等待锁期间令牌被其他线程更新的问题。
  • 轻量高效:SemaphoreSlim是单进程内的同步工具,比Mutex性能更高、资源开销更小。

额外注意事项

  • 确保_tokenLock是类的实例或静态成员,不要在方法内部声明,否则锁会完全失效。
  • 如果你的服务是多进程部署(比如负载均衡的多个实例),单进程内的锁无法解决跨进程的令牌竞争问题,此时需要结合Redis分布式锁(如Redlock)实现跨进程同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:46:27