如何将异步与锁定结合起来?

Tit*_*tan 5 .net c# asp.net asynchronous

正如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;
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,工厂功能不能异步!如果必须异步该怎么办?

附言:此答案下方评分最高(超过70票)的评论要求相同,但完全没有答复。

编辑:我认为这不是一个重复的问题,从某种意义上说,链接的答案是作者的第三方库。没有第三方代码,没有解决方案吗?也许提到SemaphoreSlim.WaitAsync可以成功使用吗?..它们各自的优缺点是什么?我们可以讨论一下吗?请重新打开问题。我不认为该问题可以通过链接的答案来解决(它只是提出了非常有主见的,主观的“ hack”)。

Cai*_*ete 8

协调对共享变量的异步访问的一种简单方法是使用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;
}
Run Code Online (Sandbox Code Playgroud)