f0r*_*0rt 23 .net c# parallel-processing multithreading
问题:我需要实现对象缓存.缓存需要是线程安全的,需要按需填充值(延迟加载).通过密钥(缓慢操作)通过Web服务检索值.所以我决定用ConcurrentDictionary其GetOrAdd() ,有一个值的工厂方法假定操作是原子操作和同步的方法.不幸的是,我在MSDN文章中找到了以下语句:如何:在ConcurrentDictionary中添加和删除项:
此外,尽管ConcurrentDictionary的所有方法都是线程安全的,但并非所有方法都是原子方法,特别是GetOrAdd和AddOrUpdate.传递给这些方法的用户委托是在字典的内部锁之外调用的.
那很不幸,但仍然没有完全回答我的回答.
问题:每个键只调用一次工厂值吗?在我的具体情况中:是否有可能寻找相同密钥的多个线程产生多个请求到Web服务的相同值?
Adi*_*ter 32
正如其他人已经指出的那样,valueFactory可能不止一次被调用.有一个通用的解决方案可以缓解这个问题 - 让您valueFactory返回一个Lazy<T>实例.虽然可能会创建多个惰性实例,但T只有在访问Lazy<T>.Value属性时才会创建实际值.
特别:
// Lazy instance may be created multiple times, but only one will actually be used.
// GetObjectFromRemoteServer will not be called here.
var lazyObject = dict.GetOrAdd("key", key => new Lazy<MyObject>(() => GetObjectFromRemoteServer()));
// Only here GetObjectFromRemoteServer() will be called.
// The next calls will not go to the server
var myObject = lazyObject.Value;
Run Code Online (Sandbox Code Playgroud)
Reed Copsey的博客文章进一步解释了这种方法
我们来看看源代码GetOrAdd:
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
if (key == null) throw new ArgumentNullException("key");
if (valueFactory == null) throw new ArgumentNullException("valueFactory");
TValue resultingValue;
if (TryGetValue(key, out resultingValue))
{
return resultingValue;
}
TryAddInternal(key, valueFactory(key), false, true, out resultingValue);
return resultingValue;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,在这种情况下,valueFactory如果两个GetOrAdd调用碰巧并行运行,很明显没有任何保证不会被调用多次.