本文介绍了从ConcurrentDictionary安全删除列表映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个ConcurrentDictionary,它将简单类型映射到列表:

  var dict = new ConcurrentDictionary< string,List<字符串>>(); 

我可以使用来满足在添加第一个值时对列表进行初始化以及将后续值添加到列表。



但是,删除操作并非如此。如果我做这样的事情:

  public void Remove(字符串键,字符串值)
{
List< ; string>清单
var found = dict.TryGetValue(key,out list);

如果(找到)
{
list.Remove(value);
if(list.Count == 0)
{
//警告:此处可能的比赛条件
dict.TryRemove(key,out list);
}
}
}

...我的意图是如果相应列表不再具有任何值,则完全删除密钥(在概念上类似于引用计数),那么我冒着竞争的风险,因为有人可能在其中添加了一些内容在我检查列表是否为空之后



尽管我在此简单示例中使用的是列表,但我通常会使用ConcurrentBag或ConcurrentDictionary在这种情况下,风险是非常相似的。

解决方案

您的 ConcurrentDictionary 受保护,但您的列表不受保护。如果可以从多个线程访问您的列表(我认为是这种情况),则需要对列表的所有访问使用锁定,或者需要使用其他构造。



在您的删除函数中调用 TryGetValue 之后,由于 List< T> 对于多线程而言并不安全,您会冒各种线程问题的风险。



如果在<$中使用嵌套的ConcurrentDictionaries c $ c> dict ,您只会遇到以下问题:删除不为空的内容-如您所写,有可能在检查项目大小后将其添加到嵌套的ConcurrentDictionary中。删除嵌套列表/字典本身是线程安全的:包含 dict ConcurrentDictionary ,它将处理删除操作物品安全。但是,如果要保证仅在列表/字典为空时才将其删除,则必须对整个操作使用锁定。



这是因为容器 dict 和嵌套的列表/字典是两个不同的结构,触摸一个对另一个没有影响-如果您需要整个多步操作是原子的,则将必须确保一次只有一个线程可以尝试执行此操作。



您的代码将如下所示:

 如果(找到)
{
锁(_listLock)
{
list.Remove(value);

if(list.Count == 0)
{
//警告:此处可能的比赛条件
dict.TryRemove(key,out list);
}
}
}

同样,如果您使用不受保护的构造(例如 List< T> ),则必须在每次访问该列表时都使用锁定。


I have a ConcurrentDictionary which maps a simple type to a list:

var dict = new ConcurrentDictionary<string, List<string>>();

I can use AddOrUpdate() to cater for both initialization of the list when the first value is added, and addition of subsequent values to the list.

However, the same isn't true for removal. If I do something like:

public void Remove(string key, string value)
{
    List<string> list;
    var found = dict.TryGetValue(key, out list);

    if (found)
    {
        list.Remove(value);
        if (list.Count == 0)
        {
            // warning: possible race condition here
            dict.TryRemove(key, out list);
        }
    }
}

...where my intention is to remove the key completely if the corresponding list no longer has any values (similar to reference counting, in concept), then I'm risking a race condition because someone might have added something to the list right after I checked whether it's empty.

Although I am using a list in this simple example, I usually have a ConcurrentBag or ConcurrentDictionary in such scenarios, and the risk is quite similar.

Is there any way of safely removing a key when the corresponding collection is empty, short of resorting to locks?

解决方案

Your ConcurrentDictionary is protected but your list is not. If your list can be accessed from multiple threads (I assume this is the case) you need to use locking around all accesses to the list or you need to use a different construct.

After you call TryGetValue in your Remove function, you access the list multiple times - since List<T> is not safe for multithreading, you run the risk of various threading issues.

If you use nested ConcurrentDictionaries in dict, you only run the problem of removing something that's not empty - as you wrote, it's possible that an item is added to the nested ConcurrentDictionary after you check its size. Removing the nested list / dictionary itself is thread-safe: the containing dict is a ConcurrentDictionary and it'll handle removing items safely. However, if you want to guarantee that a list/dictionary is only removed if it's empty, you have to use a lock around the whole operation.

This is because the container dict and the nested list/dictionary are two different constructs and touching one has no effect on the other - if you need the entire multi-step operation to be atomic, you'll have to make sure only one thread can attempt to do it at a time.

Your code would be something like this:

if (found)
{
    lock ( _listLock )
    {
        list.Remove(value);

        if (list.Count == 0)
        {
            // warning: possible race condition here
            dict.TryRemove(key, out list);
        }
    }
}

Again, if you're using an unprotected construct (like a List<T> then you have to use locking around every access to that list.

这篇关于从ConcurrentDictionary安全删除列表映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 17:12