2010-10-27 79 views
3

昨天我问了一个关于这个问题的问题,并得到了很多有用的反馈(谢谢!),但我认为我没有给出足够的信息 - 因此,另一个问题。无法安全锁定ConcurrentDictionary的值

我有两个线程同时读取两个文件。他们将来自这些文件的信息放入两个ConcurrentQueue中。另外两个线程随之出现,从ConcurrentQueues中取出项并将项放入单个ConcurrentDictionary中。更新字典中的项目时,线程可能必须创建一个新对象,或者只是通知当前对象有更多信息进来。在后一种情况下,有时会发生漫长的扫描。有时候,在这次扫描之后,对象说它可以将其删除(作为一种节省内存的尝试),并且该线程将其从字典中移除。

我现在的(碎)下面的代码:

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) 

,线程可以interuppted,所以那里有一个机会,另一个线程的时候我避开试图在这里访问它删除的对象了字典:

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

这就导致KeyNotFoundException。我需要一些自动锁定对象的方法。

正如我所说的,我昨天得到了一些建议,但我不明白他们

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

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

任何想法?很抱歉的长期职位

感谢,

弗雷德里克

+0

更新现有的文章而不是创建新的文章会更好吗?这样,所有有用的提示将会失去帮助你的新人。 – 2010-10-27 10:34:04

+0

对不起,大卫,这个网站的新手 - 在另一个线程的海报要求我发布一个新的线程与额外的信息。链接在这里:http://stackoverflow.com/questions/4025428/cant-safely-lock-a-value-of-a-concurrentdictionary – Frederik 2010-10-27 10:36:36

回答

1

你可以改变扫描线成这样:

DictionaryItemType dictionaryItem; 

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

这样你重新检查该项目是否仍然在字典中,如果它不是,只是不进入扫描分支。

+0

嗨彼得。不幸的是,我不认为这是线程安全的。两个线程可以同时输入if语句 - 第一个会调用delete,然后第二个线程不能访问它认为存在的字典中的项 – Frederik 2010-10-27 10:46:27

+0

TryGetValue也是一个原子操作,它意味着在如果你确实有'dictionaryItem'的引用。有没有我没有看到的问题? – 2010-10-27 11:01:21

+0

当然,我有一个引用,但是如果dictionaryItem不再处于集合中,我只会更新一个不再可访问的引用。换句话说,如果我正在更新某些东西,则不应允许其他人同时从集合中删除它 – Frederik 2010-10-27 11:29:44