在线程内部使用全局字典

时间:2011-12-12 22:20:32

标签: c# asp.net c#-4.0

假设我有Dictionary<string, string>。字典在我的控制台程序中声明为public static

如果我正在使用线程,并且我想从一个线程对此foreach执行Dictionary,但同时另一个线程想要将项添加到字典中。这会导致错误,因为我们在另一个线程中使用foreach循环运行它时无法修改Dictionary

为了绕过这个问题,我在字典上的每个操作上的同一个静态对象上创建了一个锁定语句。

这是绕过此问题的最佳方法吗?我的Dictionary可能非常大,我可以有许多线程想要对它进行预测。就目前而言,情况可能非常缓慢。

4 个答案:

答案 0 :(得分:7)

尝试使用专为此类情况设计的ConcurrentDictionary<TKey, TValue>

有关如何使用它的好教程here

答案 1 :(得分:1)

最大的问题是:您是否需要foreach作为快照?

如果答案是&#34;否&#34;,那么使用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处理了我们所涉及的冲突。我们获得了枚举的独占锁,但只是足够长的时间来获取列表中字典的快照,然后只在该线程中使用,而不与任何其他线程共享。

答案 2 :(得分:0)

使用.NET 4,你会得到一个新奇的ConcurrentDictionary。我认为有一些基于.NET 3.5的实现浮出水面。

答案 3 :(得分:0)

是的,当枚举在另一个线程中运行时,您将无法更新全局字典。

解决方案:

  1. 要求字典的所有用户在访问对象之前获取互斥锁,然后释放锁。
  2. 使用.NET 4.0的ConcurrentDictionary类。