Я создал асинхронный кеш, который использует .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);
}
Я думаю, что единственным недостатком является то, что мне нужно ждать ожидания за пределами блокировки, поэтому populator не является потокобезопасным, но, поскольку он не может находиться внутри замка, я думаю, что это лучший способ. Есть ли какие-то подводные камни, которые я пропустил?
Обновление: версия ответа Esers, которая также является потоковой, когда поток antoher делает недействительным кеш
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