【问题标题】:.net Dictionary.Resize() exception - thread safety?.net Dictionary.Resize() 异常 - 线程安全?
【发布时间】:2011-07-07 13:48:24
【问题描述】:


我们有一个非常严重的问题,每分钟会导致数千个异常。我们有一个网站,它运行一种本地缓存机制,以以下形式保存数据:

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

当我们在这个字典上调用 Add 时,我们得到一个非常奇怪的行为:“Index was outside the bounds of the array”,当键 100% 不在字典中时: p>

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

我们使用这样的锁来保护这个调用:

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

我们得到了这个异常:

   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)     

我们找不到任何解释,因为异常与 Dictionary 的线程安全有关,而我们(认为我们)是线程安全的。我们在每个 Add() 和 Remove() 调用上使用 lock() 或 Monitor.TryEnter,除了 m_Data.TryGetValue(...)

任何帮助将不胜感激。

非常感谢。

【问题讨论】:

  • 您也必须锁定TryGetValue,以及读取或更新字典的任何其他内容。 TryGetValue 往往会在其他线程同时添加或删除项目时表现出不可预测的行为。
  • 也许您将 generic 字典中的 TryGetValueconcurrent 字典中的 TryGetValue 混淆了,因为它们都是最近才引入的( .NET 3.5 和 .NET 4)。
  • @贾斯汀:什么? Dictionary&lt;TKey, TValue&gt;.TryGetValue 一直存在!
  • @DanTao - 哎呀,我猜我只是被msdn.microsoft.com/en-us/library/bb347013.aspx上的“其他版本”下拉菜单弄糊涂了
  • @Jim,锁定 TryGetValue 会将代码更改为 Single Reader,我希望它是 MultiReader。

标签: .net dictionary c#-3.0 thread-safety


【解决方案1】:

似乎在某些时候代码未锁定并且集合已更改...您查看过System.Collections.Concurrent 命名空间吗?特别是ConcurrentDictionary 类?它是线程安全的,并且可能会让您免于遭受诸如此类奇怪错误或竞争条件等的痛苦

它几乎像普通字典一样工作,除了对于大多数操作,您使用“尝试”方法,即TryGetValue,如果操作有效,它将尝试获取值并返回True,否则返回False ,然后您当然可以检查此值以继续您的逻辑

您应该查看这个 msdn 链接,它与您正在执行的操作非常相似:

Implementing a cache with ConcurrentDictionary

提问者当前正在使用带有ReaderWriterLockSlim 的非并发字典,并将其更改为并发字典。

【讨论】:

  • 谢谢,但我们使用 .net 3.5 编写。 ConcurrentDictionary 在 .net 4 中。
【解决方案2】:

您需要同步访问您的m_Data 字典无处不在,而不仅仅是Add 调用。你这样做了吗?

【讨论】:

  • 是的,除了调用 m_Data.TryGetValue(...)、Remove() 和 Add() 调用之外的所有地方都使用 lock() 或 Monitor.TryEnter()。谢谢!
  • @Nir:正如 Jim Mischel 在评论中指出的那样,仅锁定写入是不够的。您也必须锁定读取;否则集合可能会在在读取操作的中间被修改。
  • 锁定 TryGetValue 会将代码更改为 Single Reader,我希望它是 MultiReader。
  • @Nir:那你用错了类。 Dictionary&lt;TKey, TValue&gt; 只能支持多个并发读者当它没有被写入时。如果您没有将您的TryGetValue 调用与您的写入同步,那么所有的赌注都将被取消。如果您使用的是 .NET 3.5,我建议您下载 Reactive Extensions 库并从中使用向后移植的 ConcurrentDictionary&lt;TKey, TValue&gt; 类。
猜你喜欢
  • 2011-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-29
  • 2013-06-10
  • 2010-09-28
相关资源
最近更新 更多