Есть несколько проблем с вашим кодом.Проблемы включают: вызов Cache.Contains
за пределами lock
, в то время как другие потоки могут изменять коллекцию;вызов operation
в пределах lock
, который может вызвать взаимоблокировки;и т.д.
Вот поточно-ориентированная реализация кэша, которая удовлетворяет всем вашим требованиям:
class Cache<TKey, TValue>
{
private readonly ConcurrentDictionary<TKey, Task<TValue>> items;
public Cache()
{
this.items = new ConcurrentDictionary<TKey, Task<TValue>>();
}
public Task<TValue> GetAsync(TKey key, Func<TKey, TValue> valueFactory)
{
return this.items.GetOrAdd(key,
k => Task.Factory.StartNew<TValue>(() => valueFactory(k)));
}
}
Метод GetAsync
работает следующим образом: Сначала он проверяет, есть ли вitems
словарь для данного ключа.Если такой Задачи нет, она запускает valueFactory
в асинхронном режиме на ThreadPool
и сохраняет объект Задачи, который представляет ожидающую асинхронную операцию в словаре.Код, вызывающий GetAsync
, может дождаться завершения Задачи, что вернет значение, вычисленное как valueFactory
.Все это происходит асинхронным, неблокирующим, поточно-ориентированным способом.
Пример использования:
var cache = new Cache<string, int>();
Task<int> task = cache.GetAsync("Hello World", s => s.Length);
// ... do something else ...
task.Wait();
Console.WriteLine(task.Result);