例如,如果我有以下代码:
if (dictionary == null) {
dictionary = new ConcurrentDictionary();
}
多个线程可以读取它为空并同时创建新的ConcurrentDictionaries吗?
我说的是System.Collections.Concurrent
提供的ConcurrentDictionaryhttps://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
答案 0 :(得分:5)
ConcurrentDictionary
是线程安全的。但是你展示的代码与ConcurrentDictionary
的内部无关。 ConcurrentDictionary
以外的一切都在你的责任范围内。所以你应该确保只进行一次初始化。
一种方法是使用Lazy(T)。
Lazy<ConcurrentDictionary> _dictionary = new Lazy<ConcurrentDictionary>(() => new ConcurrentDictionary());
只需调用Value即可获取字典实例。 Lazy只会初始化一次字典,并且还会处理初始化部分的线程安全性。
var dic = _dictionary.Value; // extract instance.
答案 1 :(得分:4)
大多数构造函数都是线程安全的,因为它们只发生在一个线程上。如果他们访问静态字段或调用非线程安全的委托或其他东西,则有例外,但它们很少见(通常是个坏主意)。
这不是你问题的症结所在,因为在你的例子中,它不是可以竞赛的构造函数,而是作业。
如果另一个线程可以访问dictionary
那么确实存在争用,并且一个线程可以分配给dictionary
只是被另一个线程覆盖(并且可能有两个线程看到的句点{ {1}}具有不同的值)。
哪些可能成为问题。它可能只是次优(例如,并发字典用作缓存而某些缓存值会丢失并且必须重新计算但事情仍然有效)或者它可能是灾难性的。如果dictionary
对多个主题可见,则应锁定并重新测试,使用dictionary
或Interlocked.CompareExchange
。
答案 2 :(得分:0)
一旦它被实例化,它就是线程安全的(即多个线程可以无问题地添加/接收它)。在您的情况下,您需要创建将其实例化为线程安全的语句,您可以使用lock
关键字来执行此操作:
Object thisLock = new Object();
lock (thisLock) {
if (dictionary == null) {
dictionary = new ConcurrentDictionary();
}
}
答案 3 :(得分:0)
代码示例不是线程安全的,因为它允许多个线程查看&#34;字典&#34;在new运算符进行更改以分配新实例之前,变量为null。
使用锁定对象和锁定语句有效,但每次使用字典时都会添加锁定变量并导致锁定惩罚。
以下版本不需要锁定变量,并且在每次使用字典字段时都不会产生锁定惩罚。
if( dictionary == null )
Interlocked.CompareExchange( ref dictionary, new ConcurrentDictionary(), null );
dictionary.xxx()