从MemoryCache异步获取线程安全 [英] Async threadsafe Get from 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屋!