2011-03-02 135 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