无法安全地锁定ConcurrentDictionary值安全、ConcurrentDictionary

2023-09-06 23:13:59 作者:摇铃唤白鹿

我无法集合中锁定一个项目 - 特别是一个ConcurrentDictionary

I'm having trouble locking on an item within a Collection - specifically a ConcurrentDictionary.

我需要接受邮件,查找该消息的字典中,然后运行在一个漫长的扫描。作为该计划需要大量的内存,扫描后的对象返回真正如果他们认为它是一个很好的时间将其删除(这是我做的从词典中取出)。然而,另一个线程可以来在类似的时间和尝试后删除访问同一个对象的权利。这是我第一次尝试:

I need to accept a message, look up that message within the Dictionary and then run a lengthy scan on that. As the program takes a lot of memory, after the scan the objects return true if they think its a good time to delete it (which I do by removing it from the Dictionary). However, another thread could come at a similar time and try to access that same object right after the delete. This is my first attempt:

string dictionaryKey = myMessage.someValue;

DictionaryObject currentObject = myConcurrentDictionary.GetOrAdd(dictionaryKey, new DictionaryObject());
// we can be interrupted here
lock (currentObject)
{
    //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");
    }
}

然而,上述不工作 - 这是可能的一个线程从字典中删除对象权之前,另一个是要尝试访问该对象的字典中。看完这锁简写Monitor.Enter和Monitor.Exit 后,我试着这样的:

However, the above doesn't work - it's possible for one thread to remove an object from the Dictionary right before another is going to attempt to access that object within the Dictionary. After reading that lock is shorthand for Monitor.Enter and Monitor.Exit, I tried this:

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 still 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");
    }
}

这两种方法可能会导致一个 KeyNotFoundException 当试图查找对象了字典中。

Both ways can result in a KeyNotFoundException when trying to look the object up within the Dictionary.

有谁知道我能找到我想要锁定,然后锁定它不被打断的对象吗?对不起 - 我是新的并发和感觉彻底迷惑

Does anyone know how I could find the object I want to lock and then lock it without being interrupted? Sorry - I'm new at concurrency and feel thoroughly confused!

谢谢

弗雷德里克

推荐答案

您应该尝试同时使用它从词典中删除的对象,你开始扫描前,以prevent任何其他线程。您可以随时重新添加它,如果你要稍后,在失败扫描后()。无论删除和添加,保证线程安全这个并发收集。

You should remove the object from the dictionary before you start your scan, to prevent any other thread from trying to use it concurrently. You can always add it back in if you have to later on, after a failure in scan(). Both remove and add are guaranteed thread-safe on this concurrent collection.

这应该让你想要可以不带任何锁定 s或显示器的使用。

This should make what you want possible without any locks or Monitor usage.

string dictionaryKey = myMessage.someValue;

DictionaryObject currentObject = null;
if (myConcurrentDictionary.TryRemove(dictionaryKey, out currentObject))
{
    //KeyNotFoundException is possible on line below
    if (!currentObject.scan(myMessage)) // Scans the message - returns true if the object says its OK to remove it from the dictionary
    {
      if (!myConcurrentDictionary.TryAdd(dictionaryKey, currentObject))
       throw new Exception("Was unable to re-insert a DictionaryObject that is not OK for deletion");
    }
} 

我有这个问题,不理解你的code中的休息,是否有些其他的线程可以在另一个消息使用相同的密钥您的通话过程中添加回扫描()。这将导致 TryAdd 失败。如果这是一个可能性,更多的工作是必要的。

My concern with this, without understanding the rest of your code, is whether some other thread can add back in another message with the same key during your call to scan(). This will cause TryAdd to fail. If this is a possibility, more work is needed.

与当前模型的问题是,即使在collection是线程安全的,你如果你希望离开扫描项集合中真正要做的,是做以下的操作组合原子:1.找到一个免费的项目和2将其标记为使用中。

The problem with your current model is that even though the collection is thread-safe, what you really must do if you wish to leave the 'being scanned' items in the collection is to do the following combination of operations atomically: 1. find a free item and 2. mark it as 'in use'.

可以凭借收集是线程安全的,但的做 必须单独完成,所以你打开多个扫描()对同一个对象是个窗口。 can be done by virtue of the collection being thread-safe but has to be done separately, so you open up a window for multiple scan()s on the same object.