c# - 异步线程安全从 MemoryCache 获取

标签 c# thread-safety async-await

我创建了一个使用 .NET MemoryCache 的异步缓存。 这是代码:

public async Task<T> GetAsync(string key, Func<Task<T>> populator, TimeSpan expire, object parameters)
{
    if(parameters != null)
        key += JsonConvert.SerializeObject(parameters);

    if(!_cache.Contains(key))
    {
        var data = await populator();
        lock(_cache)
        {
            if(!_cache.Contains(key)) //Check again but locked this time
                _cache.Add(key, data, DateTimeOffset.Now.Add(expire));
        }
    }

    return (T)_cache.Get(key);
}

我认为唯一的缺点是我需要在锁外执行等待,因此填充器不是线程安全的,但由于等待不能驻留在锁内,我想这是最好的方法。是否有任何我遗漏的陷阱?

更新:当另一个线程使缓存无效时,Esers 答案的一个版本也是线程安全的:

public async Task<T> GetAsync(string key, Func<Task<T>> populator, TimeSpan expire, object parameters)
{
    if(parameters != null)
        key += JsonConvert.SerializeObject(parameters);

    var lazy = new Lazy<Task<T>>(populator, true);
    _cache.AddOrGetExisting(key, lazy, DateTimeOffset.Now.Add(expire));
    return ((Lazy<Task<T>>) _cache.Get(key)).Value;
}

但是它可能会更慢,因为它创建了永远不会执行的 Lazy 实例,并且它在完全线程安全模式下使用 Lazy LazyThreadSafetyMode.ExecutionAndPublication

更新新基准(越高越好)

Lazy with lock      42535929
Lazy with GetOrAdd  41070320 (Only solution that is completely thread safe)
Semaphore           64573360

最佳答案

一个简单的解决方案是使用 SemaphoreSlim.WaitAsync()而不是锁,然后你就可以解决在锁内等待的问题。尽管 MemoryCache 的所有其他方法都是线程安全的。

private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
public async Task<T> GetAsync(
            string key, Func<Task<T>> populator, TimeSpan expire, object parameters)
{
    if (parameters != null)
        key += JsonConvert.SerializeObject(parameters);

    if (!_cache.Contains(key))
    {
        await semaphoreSlim.WaitAsync();
        try
        {
            if (!_cache.Contains(key))
            {
                var data = await populator();
                _cache.Add(key, data, DateTimeOffset.Now.Add(expire));
            }
        }
        finally
        {
            semaphoreSlim.Release();
        }
    }

    return (T)_cache.Get(key);
}

关于c# - 异步线程安全从 MemoryCache 获取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31831860/

相关文章:

java - 我可以将 PriorityBlockingQueue 与多个线程一起使用吗?

javascript - 三个js-纹理加载-async/await

node.js - 在集合插入之前等待完成 Meteor.call

c# - 安全代码中的不安全标签

c# - 如何通过反射判断字段是否有 'new'修饰符?

c# - 如何在 asp.net core 中使用 RestSharp.NetCore

c# - 限制 HttpClient 请求速率的简单方法

c# - 如何通过普通路由重定向

c++ - 将 std::map 对象传递给线程

cocoa - NSManagedObject 实例的线程安全性?