【问题标题】:Async method in lock-statement block锁语句块中的异步方法
【发布时间】:2014-12-01 15:13:30
【问题描述】:

我想将身份验证令牌放在缓存中,以便在多个应用程序域中使用它。此令牌将每小时过期。当客户端无法使用令牌进行授权时,它会要求令牌生成服务生成一个新的。

我只希望第一个未成功验证的客户端发生这种重新生成,因此我使用了这样的锁定对象:

public async Task<Token> GenerateToken(Token oldToken)
{
    Token token;
    lock (lockObject)
    {
        var cachedToken = GetTokenFromCache();
        if (cachedToken == oldToken)
        {
            var authClient = new AuthClient(id, key);
            token = await authClient.AuthenticateClientAsync(); //KABOOM
            PutTokenInCache(token);
        }
        else
        {
            token = cachedToken;
        }
    }
    return token;
}

我的问题是AuthClient 只有async 方法,而async 方法不允许在锁语句块中使用。我对AuthClient 没有任何控制权,我可以在这里采用其他策略吗?

【问题讨论】:

  • 您使用的是 .NET 4.5 还是 .NET 4.5.1?
  • 有点像duplicate

标签: c#


【解决方案1】:

您可以使用 SemaphoreSlim 作为基本的异步就绪 lock 替换:

private readonly SemaphoreSlim lockObject = new SemaphoreSlim(1);
public async Task<Token> GenerateToken(Token oldToken)
{
  Token token;
  await lockObject.WaitAsync();
  try
  {
    var cachedToken = GetTokenFromCache();
    if (cachedToken == oldToken)
    {
      var authClient = new AuthClient(id, key);
      token = await authClient.AuthenticateClientAsync();
      PutTokenInCache(token);
    }
    else
    {
      token = cachedToken;
    }
  }
  finally
  {
    lockObject.Release();
  }
  return token;
}

【讨论】:

  • 这看起来很棒! “SemaphoreSlim 类代表了一种轻量级、快速的信号量,当等待时间预计非常短时,可用于在单个进程中等待。”如果检索令牌最多需要 5 秒,这有什么问题吗?
  • @MisterEpic:不。但是,如果您的缓存在内存中,您可以考虑缓存 task 而不是 token,这将稍微简化一下代码。
  • lock 语句的好处在于它知道从哪个线程调用它,因此lock 内的递归不是问题。 SemaphoreSlim 但是递归不兼容,所以它不是 100% 的替代品,要小心。
  • @Alex 好点。有一些尝试解决SemaphoreSlim 上的重新进入问题。在过度使用它们之前,应该仔细检查现有的嵌套锁是否是必要的。也就是说,我强烈推荐Flettu.AsyncLock 课程。它干净简单,他使用框架原生的AsyncLocal<T>,并通过异步流传递状态,就像ThreadLocal 可以为同步代码做的那样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
  • 1970-01-01
  • 2013-12-03
  • 1970-01-01
  • 2012-03-23
  • 2013-10-07
相关资源
最近更新 更多