我创建了一个使用 .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/