无法安全地锁定ConcurrentDictionary的值

时间:2010-10-27 10:28:37

标签: c# .net multithreading concurrency c#-4.0

昨天我问了一个关于这个的问题并得到了很多有用的反馈(谢谢!)但我认为我没有在问题中提供足够的信息 - 因此,另一个。

我有两个同时读取两个文件的线程。他们将这些文件中的信息放入两个ConcurrentQueues中。然后出现另外两个线程,将ConcurrentQueues中的项目出列,并将项目放入单个ConcurrentDictionary中。更新Dictionary中的项目时,线程可能必须创建一个新对象,或者只是通知当前对象有更多信息进入。在后一种情况下,有时需要进行冗长的扫描。有时,在此扫描之后,对象表示可以删除它(作为保存内存的尝试),并且线程将其从Dictionary中删除。

我目前的(破损)代码如下:

string dictionaryKey = myMessage.someValue;

Monitor.Enter(GetDictionaryLocker);
DictionaryObject currentObject = myConcurrentDictionary.GetOrAdd(dictionaryKey, new DictionaryObject());
// we can be interrupted here
lock (currentObject)
{
    Monitor.Exit(GetDictionaryLocker);
    //KeyNotFoundException is possible on line below
    if (myConcurrentDictionary[dictonaryKey].scan(myMessage)) // Scans the message - returns true if the object says its OK to remove it from the dictionary
    {
         DictionaryObject temp;                      //   It's OK to delete it
         if (!queuedMessages.TryRemove(ric, out temp))   // Did delete work?
             throw new Exception("Was unable to delete a DictionaryObject that just reported it was ok to delete it");
    }
}

这是怎么回事:

在字典中找到我想要的对象之间:

DictionaryObject currentObject = myConcurrentDictionary.GetOrAdd(dictionaryKey, new DictionaryObject());

然后锁定该对象:

lock (currentObject)

,线程可以被中断,所以当我在这里试图访问它时,另一个线程有可能将对象从字典中删除:

if (myConcurrentDictionary[dictonaryKey].scan(myMessage))

然后会导致 KeyNotFoundException 。我需要一些以原子方式锁定对象的方法。

正如我所说,我昨天得到了一些建议,但我不明白我怎么能用它们

  • 一张海报提到我应该首先尝试从Dictionary中删除项目,因为这是ConcurrentDictionary的原子操作,然后重新添加它们。但是,我不确定如何向其他线程表明它应该等待重新添加项目,而只是认为它缺少值并创建它。
  • 另一张海报Threading.Interlocked.CompareExchange,我可以用它来标记该对象是“正在使用中”。但我不知道如何处理正在使用的对象的情况 - 我将如何等待呢?

我有一些限制:我必须按顺序处理ConcurrentQueues,所以我不能放弃在Dictionary中放置一个对象,或者稍后再回来 - 我需要阻止它。字典可能包含500,000个或更多项,所以我真的需要ConcurrentDictionary的O(1)查找时间。

有什么想法吗?对于长篇文章感到抱歉

谢谢,

弗雷德里克

1 个答案:

答案 0 :(得分:1)

您可以将扫描线更改为:

DictionaryItemType dictionaryItem;

if (myConcurrentDictionary.TryGetValue(dictonaryKey, out dictionaryItem))
{
    if (dictionaryItem.scan(myMessage))

通过这种方式,您可以重新检查项目是否仍在字典中,如果不存在,则不要进入扫描分支。