2011-12-12 67 views
1

假設我有一個Dictionary<string, string>。該字典在我的控制檯程序中聲明爲public static使用線程內部的全局字典

如果我正在使用線程,並且我想在一個線程上對此Dictionary執行foreach,但同時另一個線程想將項目添加到字典中。這會導致一個bug,因爲我們不能在修改我們的Dictionary的同時在另一個線程中使用foreach循環來運行它。

要繞過此問題,我在字典上的每個操作上的同一個靜態對象上創建了一條鎖定語句。

這是繞過這個問題的最好方法嗎?我的Dictionary可能非常大,我可以有很多想要對它進行foreach的線程。就目前而言,事情可能會非常緩慢。

回答

7

嘗試使用專爲此類場景設計的ConcurrentDictionary<TKey, TValue>

關於如何使用它,有一個很好的教程here

+0

你必須小心OP所需的'foreach',因爲它不是一個快照,它可能是'ConcurrentDictionary'上的'foreach'的bug,除非你可以在工作時改變它它,或者自己添加快照。 –

0

使用.NET 4,您會看到一個花哨的新的ConcurrentDictionary。我認爲有一些基於.NET 3.5的實現浮出水面。

0

是的,當枚舉在另一個線程中運行時,您將遇到更新全局字典的問題。

解決方案:

  1. 需要字典的所有用戶在訪問對象之前獲取互斥鎖,事後解除鎖定。
  2. 使用.NET 4.0的ConcurrentDictionary類。
1

最大的問題是:你需要foreach是一個快照嗎?

如果答案是「否」,那麼使用ConcurrentDictionary,你可能會沒事的。 (剩下的一個問題是插入和讀取的性質是否以不好的方式擊中了條紋鎖,但是如果是這種情況,您會發現正常的讀取和寫入字典的情況更糟)。

但是,因爲它的GetEnumerator沒有提供快照,所以它不會在開始處列舉與末尾相同的開始。它可能會遺漏物品或重複物品。問題是這對你是否是一場災難。

如果如果您有重複項而不是其他項,那麼您可以用Distinct()(無論按鍵或按鍵和值都按要求)過濾出重複項。

如果你真的需要它是一個硬快照,那麼採取以下方法。

ConcurrentDictionarydict)和ReaderWriterLockSlimrwls)。在讀取和寫入獲得讀鎖(是的,即使你寫):

public static void AddToDict(string key, string value) 
{ 
    rwls.EnterReadLock(); 
    try 
    { 
    dict[key] = value; 
    } 
    finally 
    { 
    rwls.ExitReadLock(); 
    } 
} 
public static bool ReadFromDict(string key, out string value) 
{ 
    rwls.EnterReadLock(); 
    try 
    { 
    return dict.TryGetValue(key, out value); 
    } 
    finally 
    { 
    rwls.ExitReadLock(); 
    } 
} 

現在,當我們想枚舉字典,我們獲取寫入鎖定(即使我們正在閱讀):

public IEnumerable<KeyValuePair<string, string>> EnumerateDict() 
{ 
    rwls.EnterWriteLock(); 
    try 
    { 
    return dict.ToList(); 
    } 
    finally 
    { 
    rwls.ExitWriteLock(); 
    } 
} 

這種方式,我們獲得了讀寫共享鎖,因爲與參與,對於我們的矛盾ConcurrentDictionary交易。我們獲得枚舉的排他鎖,但時間足夠長以在列表中獲得字典的快照,然後該列表僅在該線程中使用,並且不與其他任何其他人共享。

+0

+1的詳細程度。 – Shibumi