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