2013-10-30 111 views
4

以下代碼線程安全嗎?字典更新線程安全

var dict = new Dictionary<int, string>() 
    { { 0, "" }, { 1, "" }, { 2, "" }, { 3, "" } }; 

var nums = dict.Keys.ToList(); 

Parallel.ForEach(nums, num => 
      { 
       dict[num] = LongTaskToGenerateString(); 
      }); 

return dict; 
+2

憑經驗:如果你要問,這是不是;-) – delnan

回答

5

沒有,Dictionary<TKey, TValue>類不是線程安全的修改,如可以在documentation可以看出:

一個Dictionary<TKey, TValue>可以同時支持多個閱讀器,只要收集不被修改。即便如此,枚舉集合本質上不是一個線程安全的過程。在枚舉與寫入訪問競爭的罕見情況下,集合必須在整個枚舉期間被鎖定。爲了讓集合可以被多個線程讀取和寫入,您必須實現自己的同步。

在你的情況,如果某些線程將幾乎同時完成LongTaskToGenerateString他們的字典更新會干擾。

您可以使用SyncRoot屬性來手動同步訪問,也可以僅使用ConcurrentDictionary<TKey, TValue>類,如註釋中的asawyer所示。

This實施建議,如果你只是更新現有鍵的值,它應該 OK(也看着this) - 的不雅效果可能是version屬性的準確值。它被用於防止在枚舉時修改集合,所以它最終會以哪個值來實現並不重要。不知道這方面的任何保證。

+2

ConcurrentDictionary來救援! http://msdn.microsoft.com/en-us/library/dd287191.aspx – asawyer

3

看來好像你的字典裏只有用作返回值,並且從來沒有真正用於查找鍵。在這種情況下,在計算完所有最終輸出值之前,甚至不需要字典。

所以,你可以使用PLINQ這個:

var nums = new[] { 0, 1, 2, 3 }; 

var dict = nums.AsParallel() 
       .Select(num => new KeyValuePair<int, string> 
            (num, LongTaskToGenerateString(num))); 
       .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); 

return dict;