C# 从ConcurrentDictionary安全删除列表映射

C# 从ConcurrentDictionary安全删除列表映射,c#,concurrency,race-condition,concurrentdictionary,C#,Concurrency,Race Condition,Concurrentdictionary,我有一个ConcurrentDictionary,它将简单类型映射到列表: var dict = new ConcurrentDictionary<string, List<string>>(); var dict=新的ConcurrentDictionary(); 我可以使用它在添加第一个值时初始化列表,以及在列表中添加后续值 然而,对于移除,情况并非如此。如果我这样做: public void Remove(string key, string value) {

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

var dict = new ConcurrentDictionary<string, List<string>>();
var dict=新的ConcurrentDictionary();
我可以使用它在添加第一个值时初始化列表,以及在列表中添加后续值

然而,对于移除,情况并非如此。如果我这样做:

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);
        }
    }
}
public void Remove(字符串键、字符串值)
{
名单;
var found=dict.TryGetValue(输入,输出列表);
如果(找到)
{
列表。删除(值);
如果(list.Count==0)
{
//警告:此处可能存在竞争条件
dict.TryRemove(键,输出列表);
}
}
}
…如果我的意图是如果相应的列表不再有任何值,则完全删除该键(概念上类似于引用计数),那么我冒着竞争条件的风险,因为在我检查列表是否为空后,可能有人在列表中添加了一些内容

虽然我在这个简单的例子中使用了一个列表,但在这种情况下,我通常会有一个ConcurrentBag或ConcurrentDictionary,风险非常相似。


当相应的集合为空时,除了使用锁之外,还有什么方法可以安全地删除密钥?

您的
ConcurrentDictionary
受到保护,但您的列表没有受到保护。如果可以从多个线程访问列表(我假设是这样),则需要对列表的所有访问使用锁定,或者需要使用不同的构造

Remove
函数中调用
TryGetValue
后,您可以多次访问该列表-由于
list
对于多线程来说不安全,因此您可能会遇到各种线程问题

如果在
dict
中使用嵌套的ConcurrentDictionary,则只会出现删除非空项的问题—正如您所写,在检查嵌套的ConcurrentDictionary的大小后,可能会将项添加到该字典中。删除嵌套列表/字典本身是线程安全的:包含的
dict
是一个
concurrentdirectionary
,它将安全地处理删除项的操作。但是,如果您想保证列表/字典只有在为空时才会被删除,那么必须在整个操作中使用锁

这是因为容器
dict
和嵌套列表/字典是两个不同的构造,触摸其中一个对另一个没有影响-如果您需要整个多步骤操作是原子的,您必须确保一次只能有一个线程尝试这样做

您的代码如下所示:

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

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

同样,如果您使用的是未受保护的构造(就像一个
列表
那么你必须在每个访问该列表的地方使用锁定。

调用
TryGetValue
删除
之间是否也存在潜在的竞争?我可能错了,但我不这么认为……如果有人在空性检查之前添加了一些内容,那么它就不会是空的……如果他们调用删除呢?如果两个瘦的同时删除gs,然后其中一个发现列表为空并将其删除,这是预期的行为。真正的危险是由于竞争条件而丢失一个不为空的列表。我想知道锁是否需要再高一点-即在
找到之前set@doctorlove我不这么认为(但我并不想在这方面听起来像是权威:)。一旦
TryGetValue
返回,您就持有对列表的引用,这样列表就不会发生任何事情(它不会消失,等等).
found
是一个局部变量,其他线程无法处理它。应该是原子的实际工作发生在
if
块内,因此我认为锁定它就足够了。如果您使用
锁来保护
TryRemove
,那么您必须在每次访问并发dic时使用相同的
lock
默认。否则,这个单一的
将一事无成。@TheodorZoulias我不相信在这种情况下,
ConcurrentDictionary
需要锁。锁已到位,因为列表实例已被修改,同一列表可能同时在另一个线程中被修改。从字典中获取/删除列表实例ary不需要锁;
TryRemove()
调用位于锁内,因为
if()
作用于必须保护的列表实例。您认为我的推理有缺陷吗?我猜项目可以从另一个线程同时插入列表中。如果您不使用相同的锁保护插入项目的代码,则可以在行
If(list.Count==0)后添加项目
在第
行dict.TryRemove(key,out list)
之前。在这种情况下,将从字典中删除一个非空列表。这不是问题吗?