我想使用带有ConcurrentDictionary的GetOrAdd之类的东西作为use服务的缓存。这个字典有异步版本吗?GetOrAdd将使用HttpClient发出web请求,所以如果这个字典有一个版本的GetOrAdd是异步的,那就太好了。
为了澄清一些混淆,字典的内容将是对the服务的调用的响应。
ConcurrentDictionary<string, Response> _cache
= new ConcurrentDictionary<string, Response>();
var response = _cache.GetOrAdd("id",
(x) => { _httpClient.GetAsync(x).GetAwaiter().GetResponse(); });发布于 2019-01-10 04:53:25
GetOrAdd不会成为异步操作,因为访问字典的值不是一个长时间运行的操作。
但是,您可以做的只是将任务存储在字典中,而不是物化的结果。然后,任何需要结果的人都可以等待该任务。
但是,您还需要确保操作只启动一次,而不是多次启动。要确保某些操作只运行一次,而不是多次运行,还需要添加Lazy
ConcurrentDictionary<string, Lazy<Task<Response>>> _cache = new ConcurrentDictionary<string, Lazy<Task<Response>>>();
var response = await _cache.GetOrAdd("id", url => new Lazy<Task<Response>>(_httpClient.GetAsync(url))).Value;发布于 2019-01-10 04:37:04
对于此目的,使用GetOrAdd方法并不是很好。因为它不能保证工厂只运行一次,所以它的唯一目的是一个小的优化(小的,因为添加无论如何都是很少的),因为它不需要散列和找到正确的存储桶两次(这将发生两次,如果你用两个单独的调用获取和设置)。
我建议您首先检查缓存,如果在缓存中没有找到值,则输入某种形式的临界区(锁、信号量等),重新检查缓存,如果仍未找到,则获取该值并插入到缓存中。
这确保后备存储只被命中一次;即使多个请求同时获得缓存未命中,也只有第一个请求会实际获取该值,其他请求将等待信号量,然后提前返回,因为它们会重新检查临界区中的缓存。
Psuedo代码(使用计数为1的SemaphoreSlim,因为您可以异步等待它):
async Task<TResult> GetAsync(TKey key)
{
// Try to fetch from catch
if (cache.TryGetValue(key, out var result)) return result;
// Get some resource lock here, for example use SemaphoreSlim
// which has async wait function:
await semaphore.WaitAsync();
try
{
// Try to fetch from cache again now that we have entered
// the critical section
if (cache.TryGetValue(key, out result)) return result;
// Fetch data from source (using your HttpClient or whatever),
// update your cache and return.
return cache[key] = await FetchFromSourceAsync(...);
}
finally
{
semaphore.Release();
}
}发布于 2020-12-12 20:39:40
尝试此扩展方法:
/// <summary>
/// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey, TValue}"/> by using the specified function
/// if the key does not already exist. Returns the new value, or the existing value if the key exists.
/// </summary>
public static async Task<TResult> GetOrAddAsync<TKey,TResult>(
this ConcurrentDictionary<TKey,TResult> dict,
TKey key, Func<TKey,Task<TResult>> asyncValueFactory)
{
if (dict.TryGetValue(key, out TResult resultingValue))
{
return resultingValue;
}
var newValue = await asyncValueFactory(key);
return dict.GetOrAdd(key, newValue);
}您使用await dict.GetOrAddAsync(key,async key=>await something(key))而不是dict.GetOrAdd(key,key=>something(key))。显然,在这种情况下,您只需将其编写为await dict.GetOrAddAsync(key,something),但我想说明一下。
有关维持运作秩序的疑虑,我有以下观察所得:
如果你观察它的实现方式,使用普通GetOrAdd的
在锁的外部调用valueFactory委托,以避免在锁下执行未知代码时可能出现的问题。因此,对于ConcurrentDictionary类上的所有其他操作,GetOrAdd不是原子的
https://stackoverflow.com/questions/54117652
复制相似问题