Возможно, используется выделенный кеш памяти (например, new или старый MemoryCache
классы или эту стороннюю библиотеку) предпочтительнее к использованию простого ConcurrentDictionary
. Если вам действительно не нужны часто используемые функции, такие как истечение срока действия, сжатие на основе размера, автоматическое исключение записей, которые зависят от других записей, срок действия которых истек, или зависят от изменяемых внешних ресурсов (например, файлов, баз данных и т. Д.). Однако следует отметить, что _3 _ все еще может потребоваться некоторая работа для правильной обработки асинхронных делегатов, поскольку его нестандартное поведение не идеально.
Ниже приведен собственный метод расширения GetOrAddAsync
для ConcurrentDictionary
, которые имеют Task<TValue>
значения. Он принимает фабричный метод и гарантирует, что метод будет вызван не более одного раза. Это также гарантирует, что неудачные задачи будут удалены из словаря. Эта реализация оптимизирована для случая, когда получение существующей задачи происходит часто, а создание новой - редко.
/// <summary>
/// Returns an existing task from the concurrent dictionary, or adds a new task
/// using the specified asynchronous factory method. Concurrent invocations for
/// the same key are prevented, unless the task is removed before the completion
/// of the delegate. Failed tasks are evicted from the concurrent dictionary.
/// </summary>
public static Task<TValue> GetOrAddAsync<TKey, TValue>(
this ConcurrentDictionary<TKey, Task<TValue>> source, TKey key,
Func<TKey, Task<TValue>> valueFactory)
{
if (!source.TryGetValue(key, out var currentTask))
{
Task<TValue> newTask = null;
var newTaskTask = new Task<Task<TValue>>(async () =>
{
try { return await valueFactory(key).ConfigureAwait(false); }
catch
{
((ICollection<KeyValuePair<TKey, Task<TValue>>>)source)
.Remove(new KeyValuePair<TKey, Task<TValue>>(key, newTask));
//source.TryRemove(KeyValuePair.Create(key, newTask)); // .NET 5
throw;
}
});
newTask = newTaskTask.Unwrap();
currentTask = source.GetOrAdd(key, newTask);
if (currentTask == newTask)
newTaskTask.RunSynchronously(TaskScheduler.Default);
}
return currentTask;
}
Пример использования:
var cache = new ConcurrentDictionary<string, Task<HttpResponseMessage>>();
var response = await cache.GetOrAddAsync("https://stackoverflow.com", async url =>
{
return await _httpClient.GetAsync(url);
});
Для удаления проблемных задач эта реализация использует явно реализованный _ 9_. Более подробную информацию об этом API можно найти здесь. Начиная с .NET 5 и далее, новый
person
Theodor Zoulias
schedule
13.01.2021
GetOrAdd
не имеет большого смысла. Этот метод может выполняться только синхронно. - person Yeldar Kurmangaliyev   schedule 09.01.2019GeOrAdd
с результатом. В конечном итоге вам придется снова проверить, если что-то еще вставило ключ, пока вы ждали ввода-вывода. - person juharr   schedule 09.01.2019ConcurrentDictionary
. Он начинается с проверки, затем генерирует новое значение, а затем снова проверяет перед добавлением. - person Poul Bak   schedule 08.06.2021