制作快照并清空 ConcurrentDictionary

问题描述

我有一个想要清空的 ConcurrentDictionary,将其内容复制到字典(或任何其他容器)中,同时清空它。

一个关键的不变量是在操作过程中没有来自源的元素丢失。

我当前的代码如下所示:

    private Dictionary<TKey,TValue> Foo<TKey,TValue>(ConcurrentDictionary<TKey,TValue> source)
    {
        var result = new Dictionary<TKey,TValue>();
        foreach (var item in source)
        {
            result[item.Key] = item.Value;
        }
        source.Clear();
        return result;
    }

根据我的理解,这段代码是线程安全的,但是在 foreach 循环之后和 Clear() 之前并发添加的任何元素都将被清除。

编辑:更精确一些。在我的用例中,该代码是唯一删除某些字典键的代码;其他线程只使用 TryAdd 或 AddOrUpdate。

解决方法

我能想到的唯一方法是TryRemove所有现有的键,一个一个:

public static Dictionary<TKey,TValue> RemoveAll<TKey,TValue>(
    this ConcurrentDictionary<TKey,TValue> source)
{
    var result = new Dictionary<TKey,TValue>();
    foreach (var key in source.Keys)
    {
        if (source.TryRemove(key,out var value)) result.Add(key,value);
    }
    return result;
}

Keys 属性返回 ConcurrentDictionary 中键的快照,因此不可能包含两次相同的键。但它会产生争用,因为它acquires all internal locks。下面是一个使用 ConcurrentDictionary 的惰性枚举器的版本,因此它不会产生太多争用:

public static Dictionary<TKey,TValue>();
    foreach (var entry in source)
    {
        var (key,value) = entry;
        if (!result.ContainsKey(key) && source.TryRemove(key,out value))
            result.Add(key,value);
    }
    return result;
}

!result.ContainsKey(key) 的原因是枚举器产生两次相同键的(理论上)可能性。基于 ConcurrentDictionarythis cannot happen 的当前实现。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...