看這么幾句解釋(英文原帖):
private static void ConcurrentDictionary() { var dict = new ConcurrentDictionary<int, string>(); ThreadPool.QueueUserWorkItem(LongGetOrAdd(dict, 1)); ThreadPool.QueueUserWorkItem(LongGetOrAdd(dict, 1)); } private static WaitCallback LongGetOrAdd(ConcurrentDictionary<int, string> dict, int index) { return o => dict.GetOrAdd(index,i => { Console.WriteLine("Adding!"); Thread.SpinWait(1000); return i.ToString(); } ); }
1) threadA calls GetOrAdd, finds no item and creates a new item to Add by invoking the valueFactory delegate.
線程A調用GetOrAdd,發現數據不存在並創建了一條新數據,准備調用委托方法添加數據
2) threadB calls GetOrAdd concurrently, its valueFactory delegate is invoked and it arrives at the internal lock before threadA, and so its new key-value pair is added to the dictionary.
線程B現在也在調用GetOrAdd方法,它的委托被調用了,比線程A先進入GetOrAdd內部的鎖,因此它創建的鍵值對被添加到dict中了
3) threadA’s user delegate completes, and the thread arrives at the lock, but now sees that the item exists already
線A的委托執行完成了,並且也獲得了GetOrAdd的鎖,但是現在發現相關數據已經存在了
4) threadA performs a "Get", and returns the data th at was previously added by threadB.
線程A執行“Get”,返回之前被線程B添加的數據
不止這個英文原帖,好多國內的帖子都在說這個問題。
然而我覺得這並不是個問題,這最多是一個編程時需要注意的事項,微軟的這個設計是合理的:ConcurrentDictionary沒有理由也沒有職責去把外部代碼加鎖,外部代碼的同步應該由
外部控制。回頭看看上面的解釋,我們發現只不過是傳遞給ConcurrentDictionary的委托被執行了不止一次,但數據仍然只添加了一次,ConcurrentDictionary已經完全履行了自己的職責,而且MSDN也作了相關的說明(事實上,上面的四句英文也來自MSDN):
The user delegate that is passed to these methods is invoked outside of the dictionary's internal lock. (This is done to prevent unknown code from blocking all threads.)
傳遞進來的用戶委托在字典內部鎖的外面調用,這么做是為了防止阻塞所有相關線程,具體參考MSDN。
