有關於線程安全性的問題ConcurrentDictionary
。從API中,我看到枚舉器是線程安全的,但對於鍵和值屬性看不到。我的問題是:是ConcurrentDictionary鍵或值屬性線程安全
當有其他線程同時修改它時,是否可以安全地循環使用Keys
或Values
集合?
有關於線程安全性的問題ConcurrentDictionary
。從API中,我看到枚舉器是線程安全的,但對於鍵和值屬性看不到。我的問題是:是ConcurrentDictionary鍵或值屬性線程安全
當有其他線程同時修改它時,是否可以安全地循環使用Keys
或Values
集合?
ConcurrentDictionary表示一個線程安全收集鍵值 對可以由多個線程同時訪問的。
來源:MSDN
+1,簡潔! :)另外:是的,這兩個屬性都是用一個鎖保護的,你要枚舉的是當你開始枚舉時字典的內容**(所以你可能會看到一個鍵,例如,已經從另一個線程中刪除)。 –
雖然我不喜歡的文檔,我傾向於在有疑問時用小程序來驗證的東西或我覺得我可能會承擔太多。
以下代碼將驗證您確實可以安全枚舉values集合,同時將一個單獨的線程中的鍵添加或移除到枚舉正在發生的位置。這不會導致通常收集被修改的異常。更詳細地,這裏有一對夫婦的測試用例
案例1:枚舉值和刪除鍵
如果你遵循以下順序:
觀察到的行爲是,當我們開始枚舉時,刪除的鍵確實會被枚舉,因爲它存在於值集合中。將不會引發異常。
案例2:枚舉值和增加的關鍵
觀察到的行爲是添加的鍵不會被枚舉,因爲它d當我們開始列舉它時,id在值集合中不存在。無論我們使用TryAdd還是通過直接分配字典即詞典[key] = value來添加,都不會引發異常。
示例代碼
下面是一個說明這兩種情況下的示例程序:
ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>();
// Seed the dictionary with some arbitrary values;
for (int i = 0; i < 30; i++)
{
dictionary.TryAdd(i, i);
}
// Reader thread - Enumerate the Values collection
Task.Factory.StartNew(
() =>
{
foreach (var item in dictionary.Values)
{
Console.WriteLine("Item {0}: count: {1}", item, dictionary.Count);
Thread.Sleep(20);
}
}
);
// writer thread - Modify dictionary by adding new items and removing existing ones from the end
Task.Factory.StartNew(
() =>
{
for (int i = 29; i >= 0; i--)
{
Thread.Sleep(10);
//Remove an existing entry
int removedValue;
if (dictionary.TryRemove(i, out removedValue))
Console.WriteLine("Removed item {0}", removedValue);
else
Console.WriteLine("Did not remove item {0}", i);
int iVal = 50 + i*2;
dictionary[iVal] = iVal;
Thread.Sleep(10);
iVal++;
dictionary.TryAdd(iVal, iVal);
}
}
);
Console.ReadKey();
這裏是在釋放模式的輸出:
希望微軟可以在MSDN中記錄這一點。 – user664769
你真正說的是'Keys'和'Values'集合是當你獲得枚舉器時字典的快照。幾乎就像他們做了一個深刻的克隆,並把它交給你來列舉。 –
枚舉行爲與Keys和Values不同operties。雖然這些提供了字典的即時快照,但由['GetEnumerator'](https://msdn.microsoft.com/en-us/library/dd287131.aspx)返回的內容(也用於使用字典作爲源的LINQ查詢)包含在調用GetEnumerator後對字典進行的修改。 –