正如着名的blog post from Stephen Cleary所指示的那样,不应该尝试同步运行异步代码(例如,通过Task.RunSynchronously()
或访问Task.Result
)。另一方面,您不能在lock
语句中使用async / await。
我的用例是ASP.NET Core应用程序,它使用IMemoryCache来缓存一些数据。现在,当数据不可用时(例如缓存被丢弃)我必须重新填充它,并且应该使用lock
进行保护。
public TItem Get<TItem>(object key, Func<TItem> factory)
{
if (!_memoryCache.TryGetValue(key, out TItem value))
{
lock (_locker)
{
if (!_memoryCache.TryGetValue(key, out value))
{
value = factory();
Set(key, value);
}
}
}
return value;
}
在此示例中,工厂函数不能异步!如果它必须是异步的,该怎么办?
P.S。:this answer以下评分最高(70+票)的评论同样如此,但根本没有回复。
编辑:我认为这不是一个重复的问题,从某种意义上说,链接的答案是作者的第三方库。没有第三方代码没有解决方案吗?也许提到SemaphoreSlim.WaitAsync
可以成功使用?..每个人的利弊是什么?我们可以进行讨论吗?请重新打开这个问题。我不认为这个问题是通过链接的答案解决的(它只是提出了非常自以为是的主观“黑客”)。
答案 0 :(得分:5)
协调对共享变量的异步访问的一种简单方法是使用SemaphoreSlim
。您调用WaitAsync
开始异步锁定,然后调用Release
结束异步锁定。
例如
private static readonly SemaphoreSlim _cachedCustomersAsyncLock = new SemaphoreSlim(1, 1);
private static ICollection<Customer> _cachedCustomers;
private async Task<ICollection<Customer>> GetCustomers()
{
if (_cachedCustomers is null)
{
await _cachedCustomersAsyncLock.WaitAsync();
try
{
if (_cachedCustomers is null)
{
_cachedCustomers = GetCustomersFromDatabase();
}
}
finally
{
_cachedCustomersAsyncLock.Release();
}
}
return _cachedCustomers;
}