gpt4 book ai didi

c# - 为什么 ConcurrentDictionary.GetOrAdd(key, valueFactory) 允许 valueFactory 被调用两次?

转载 作者:IT王子 更新时间:2023-10-29 04:18:43 24 4
gpt4 key购买 nike

我将并发字典用作线程安全的静态缓存并注意到以下行为:

来自 the MSDN docs on GetOrAdd :

If you call GetOrAdd simultaneously on different threads, addValueFactory may be called multiple times, but its key/value pair might not be added to the dictionary for every call.

我希望能够保证工厂只被调用一次。有没有什么方法可以使用 ConcurrentDictionary API 来执行此操作而无需诉诸我自己的单独同步(例如,锁定在 valueFactory 中)?

我的用例是 valueFactory 在动态模块中生成类型,所以如果同一个键的两个 valueFactory 同时运行,我会点击:

System.ArgumentException:程序集中类型名称重复。

最佳答案

您可以使用这样输入的字典:ConcurrentDictionary<TKey, Lazy<TValue>> ,然后您的值(value)工厂将返回 Lazy<TValue>已使用 LazyThreadSafetyMode.ExecutionAndPublication 初始化的对象,这是 Lazy<TValue> 使用的默认选项如果你不指定它。通过指定 LazyThreadSafetyMode.ExecutionAndPublication你告诉 Lazy 只有一个线程可以初始化和设置对象的值。

这导致 ConcurrentDictionary仅使用 Lazy<TValue> 的一个实例对象,以及 Lazy<TValue>对象保护多个线程免于初始化其值。

var dict = new ConcurrentDictionary<int, Lazy<Foo>>();
dict.GetOrAdd(key,
(k) => new Lazy<Foo>(valueFactory)
);

缺点是每次访问字典中的对象时都需要调用 *.Value。这里有一些 extensions这会有所帮助。

public static class ConcurrentDictionaryExtensions
{
public static TValue GetOrAdd<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, Func<TKey, TValue> valueFactory
)
{
return @this.GetOrAdd(key,
(k) => new Lazy<TValue>(() => valueFactory(k))
).Value;
}

public static TValue AddOrUpdate<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, Func<TKey, TValue> addValueFactory,
Func<TKey, TValue, TValue> updateValueFactory
)
{
return @this.AddOrUpdate(key,
(k) => new Lazy<TValue>(() => addValueFactory(k)),
(k, currentValue) => new Lazy<TValue>(
() => updateValueFactory(k, currentValue.Value)
)
).Value;
}

public static bool TryGetValue<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, out TValue value
)
{
value = default(TValue);

var result = @this.TryGetValue(key, out Lazy<TValue> v);

if (result) value = v.Value;

return result;
}

// this overload may not make sense to use when you want to avoid
// the construction of the value when it isn't needed
public static bool TryAdd<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, TValue value
)
{
return @this.TryAdd(key, new Lazy<TValue>(() => value));
}

public static bool TryAdd<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, Func<TKey, TValue> valueFactory
)
{
return @this.TryAdd(key,
new Lazy<TValue>(() => valueFactory(key))
);
}

public static bool TryRemove<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, out TValue value
)
{
value = default(TValue);

if (@this.TryRemove(key, out Lazy<TValue> v))
{
value = v.Value;
return true;
}
return false;
}

public static bool TryUpdate<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> @this,
TKey key, Func<TKey, TValue, TValue> updateValueFactory
)
{
if (!@this.TryGetValue(key, out Lazy<TValue> existingValue))
return false;

return @this.TryUpdate(key,
new Lazy<TValue>(
() => updateValueFactory(key, existingValue.Value)
),
existingValue
);
}
}

关于c# - 为什么 ConcurrentDictionary.GetOrAdd(key, valueFactory) 允许 valueFactory 被调用两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12611167/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com