从MemoryCache异步获取线程安全 [英] Async threadsafe Get from MemoryCache

查看:227
本文介绍了从MemoryCache异步获取线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个异步缓存,该缓存在下面使用.NET MemoryCache. 这是代码:

I have created a async cache that uses .NET MemoryCache underneath. This is the code:

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);
}

我认为唯一的缺点是我需要在锁外进行等待,以便填充器不是线程安全的,但是由于等待不能驻留在锁内,我想这是最好的方法.我错过了任何陷阱吗?

I think the only downside is that I need to do the await outside the lock so the populator isn't thread safe, but since the await can't reside inside a lock I guess this is the best way. Are there any pitfalls that I have missed?

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

Update: A version of Esers answer that is also threadsafe when another thread invalidates the cache:

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

It can however be slower because it creates Lazy instances that never will be executed and it uses Lazy in full threadsafe mode LazyThreadSafetyMode.ExecutionAndPublication

使用新基准进行更新(越高越好)

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

推荐答案

一个简单的解决方案是使用 SemaphoreSlim.WaitAsync() 而不是锁,这样您就可以解决在锁中等待的问题.但是,MemoryCache的所有其他方法都是线程安全的.

A simple solution would be to use SemaphoreSlim.WaitAsync() instead of a lock, and then you could get around the issue of awaiting inside a lock. Although, all other methods of MemoryCache are thread-safe.

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);
}

这篇关于从MemoryCache异步获取线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆