2011-03-02 137 views
3


我們有一個非常嚴重的問題,每分鐘造成數千例外。我們已經有了運行在的形式保存數據的本土緩存機制網站:.net Dictionary.Resize()異常 - 線程安全嗎?

protected static IDictionary<int, IList<IInterfaceForData>> m_Data = null; 

當我們調用添加這個字典中,我們得到了一個非常奇怪的現象:「指數外

if(Monitor.TryEnter(m_LockObj, 1000)) 
{ 
    try 
    { 
     m_Data.Add(id, new List<IInterfaceForData>()); 
    } 
    catch(Exception ex) 
    {         
     // log exception 
    } 
    finally 
    { 
     Monitor.Exit(m_LockObj); 
    } 
} 

m_Data.Add(id, new List<IInterfaceForData>()); 

我們使用這樣的鎖保護這一呼籲:數組」,當關鍵是100%沒有在字典中的邊界我們得到這個異常:

at System.Collections.Generic.Dictionary`2.Resize()  at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)  at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)  

我們找不到任何解釋這是因爲異常有關詞典的線程安全的,我們(我們認爲)是線程安全的。我們對每個Add()和Remove()調用使用lock()或Monitor.TryEnter,除了m_Data.TryGetValue(...)

任何幫助將不勝感激。

非常感謝。

+2

您還必須鎖定'TryGetValue',以及讀取或更新字典的其他內容。當某個其他線程同時添加或刪除一個項目時,TryGetValue'傾向於表現不可預知的行爲。 – 2011-03-02 21:05:12

+0

也許你正在將* generic *字典上的'TryGetValue'與* concurrent *字典上的TryGetValue混淆,因爲它們都是最近才引入的(.NET 3.5和.NET 4)。 – Justin 2011-03-02 21:19:20

+0

@Justin:哇? '詞典 .TryGetValue'已經存在了! – 2011-03-02 22:41:50

回答

6

它似乎在某些時候代碼沒有被鎖定,集合被改變了...你看看System.Collections.Concurrent命名空間嗎?具體是ConcurrentDictionary類嗎?這是線程安全的,可能會節省你從這種奇怪的錯誤或競爭條件等很多痛苦等

它的作品幾乎像普通字典,除了大多數操作,你使用「嘗試」的方法,即TryGetValue這將嘗試獲得該值,並返回True如果操作有效並且False如果不是,則可以然後檢查該值以繼續您的邏輯

您應該檢出此msdn鏈接,它的確與你在做什麼:

Implementing a cache with ConcurrentDictionary

提問者當前正在使用一個帶有ReaderWriterLockSlim的非併發字典,並將其更改爲併發字典。

+0

謝謝,但我們寫在.net 3.5。 ConcurrentDictionary在.net 4中。 – Nir 2011-03-06 12:11:56

4

您需要同步訪問您的m_Data字典無處不在,而不僅僅是Add調用。你在做那個嗎?

+0

是的,在除了調用m_Data.TryGetValue(...)之外的任何地方,Remove()和Add()調用都使用lock()或Monitor.TryEnter()。謝謝! – Nir 2011-03-02 20:49:03

+1

@Nir:正如Jim Mischel在評論中指出的那樣,僅鎖定寫入就不夠好。你也必須鎖定閱讀;否則可能會在*讀取操作中修改集合*。 – 2011-03-02 22:43:04

+0

對TryGetValue進行鎖定會將代碼更改爲Single Reader,並且我希望它成爲MultiReader。 – Nir 2011-03-06 12:12:32