asp.net适当的异步锁定每个缓存键

时间:2017-12-08 10:03:07

标签: c# asp.net caching async-await locking

我正在阅读伟大的AsyncLock library,但我有一些问题。

链接示例包含以下代码行:

private readonly AsyncLock _mutex = new AsyncLock();
public async Task DoStuffAsync()
{
  using (await _mutex.LockAsync())
  {
    await Task.Delay(TimeSpan.FromSeconds(1));
  }
}

我尝试在ASP.NET控制器中重用此代码,但我有些疑惑。

上面提到的场景包括bacic锁定,其中缓存键总是相同的,但如果我有动态缓存,如下例所示,我不希望ArticleId1锁定用户加载ArticleId2 。 每个缓存密钥是否都有自己的AsyncLock _mutex

另外,我是否已正确将互斥锁转换为静态,因为多个用户将分享锁?

private static readonly AsyncLock _mutexIndex = new AsyncLock();
public async Task<IActionResult> Index(int articleId)
{

    var key = CacheKeysFor.Article.ById(articleId);
    ArticleModel cacheEntry;
    cacheEntry = _cache.Get<ArticleModel>(key);

    if (cacheEntry == null)
    {
        using (await _mutexIndex.LockAsync())
        {
            if (cacheEntry == null)
            {
                cacheEntry = SomeDatabaseCall
                _cache.Set(key, cacheEntry, TimeSpan.FromSeconds(60));
            }

        }
    }

    return View(cacheEntry);
}

1 个答案:

答案 0 :(得分:2)

如果你要走这条路线 - 是的,你需要为每把钥匙单独锁。例如,您可以使用ConcurrentDictionary

来实现这一目标
static ConcurrentDictionary<string, Lazy<AsyncLock>> _keyLocks = new ConcurrentDictionary<string, Lazy<AsyncLock>>();

public async Task<IActionResult> Index(int articleId)
{    
    var key = CacheKeysFor.Article.ById(articleId);
    ArticleModel cacheEntry;
    cacheEntry = _cache.Get<ArticleModel>(key);

    if (cacheEntry == null)
    {
        var keyLock = _keyLocks.GetOrAdd(key, _ => new Lazy<AsyncLock>(() => new AsyncLock())).Value;
        using (await keyLock.LockAsync())
        {
            if (cacheEntry == null)
            {
                cacheEntry = SomeDatabaseCall
                _cache.Set(key, cacheEntry, TimeSpan.FromSeconds(60));
            }

        }
    }

    return View(cacheEntry);
}

请注意,密钥锁会随着时间的推移而累积。除非你有数百万的问题,否则这不是一个大问题。如果你确实拥有数百万 - 你可以不时清除锁定。这样做并不安全,并且可能允许多个线程进入受保护的块,但在这个具体的情况下,它似乎也不是问题(因为你使用锁来避免昂贵的数据库调用)。