如何将新元素添加到作为 ConcurrentDictionary 值的哈希集中?

How to add a new element to a hashset that is value of a ConcurrentDictionary?

我有一个 ConcurrentDictionary,它的键是 long,值是 int 哈希集。我希望如果键不在字典中,则添加一个包含第一个元素的新哈希集。如果键存在,则将新元素添加到现有字典中。

我正在尝试这样的事情:

ConcurrentDictionary<long, HashSet<int>> myDic = new ConcurrentDictionary<long, HashSet<int>>();
int myElement = 1;
myDic.AddOrUpdate(1, new Hashset<int>(){myFirstElement},
(key, actualValue) => actualValue.Add(myElement));

此代码的问题在于第三个参数,因为 .Add() 方法 returns 是一个布尔值,而 AddOrUpdate 需要一个哈希集。第一和第二个参数对

所以我的问题是如何以线程安全的方式向散列集添加新元素并避免重复(这就是我使用散列集作为值的原因)。 hashset 的问题是它不是线程安全的,如果我先获取它然后添加新元素,我在字典之外做,我可能会遇到问题。

谢谢。

如果将匿名函数定义包裹在大括号中,则可以在函数主体中定义多个语句,从而指定 return 值,如下所示:

myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement },
(key, actualValue) => {
    actualValue.Add(myElement);
    return actualValue;
});

要修复编译器错误,您可以这样做:

myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement },
    (key, actualValue) => {
        actualValue.Add(myFirstElement);
        return actualValue;
    });

BUT 这不是线程安全的,因为 "update" 函数不在任何锁内 运行 所以你可能会添加非线程安全的HashSet 来自多个线程。这可能会导致(例如)丢失值(因此您向 HashSet 添加了 1000 个项目,但最终您只有 970 个项目)。 AddOrUpdate 中的更新函数不应该有任何副作用,这里有。

您可以在向 HashSet:

添加值时锁定自己
myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement },
    (key, actualValue) => {
        lock (actualValue) {
            actualValue.Add(myFirstElement);
            return actualValue;
        }
    });

但接下来的问题是您为什么首先使用无锁结构 (ConcurrentDictionary)。除此之外 - 任何其他代码都可能从您的字典中获取 HashSet 并在没有任何锁定的情况下在那里添加值,从而使整个事情变得无用。因此,如果您出于某种原因决定采用这种方式 - 您必须确保在从该字典访问 HashSet 时所有代码都锁定。

而不是所有这些 - 只需使用并发收集而不是 HashSet。据我所知,没有 ConcurrentHashSet,但您可以使用另一个带有虚拟键的 ConcurrentDictionary 作为替代(或在互联网上查找自定义实现)。

旁注。这里

myDic.AddOrUpdate(1, new Hashset<int>(){myFirstElement}, 

每次调用 AddOrUpdate 时都会创建新的 HashSet,即使不需要该字典,因为键已经存在。而是使用带有附加值工厂的重载:

myDic.AddOrUpdate(1, (key) => new HashSet<int>() { myFirstElement },

编辑:ConcurrentDictionary 作为哈希集的示例用法:

var myDic = new ConcurrentDictionary<long, ConcurrentDictionary<int, byte>>();
long key = 1;
int element = 1;
var hashSet = myDic.AddOrUpdate(key, 
    _ => new ConcurrentDictionary<int, byte>(new[] {new KeyValuePair<int, byte>(element, 0)}),
    (_, oldValue) => {
        oldValue.TryAdd(element, 0);
        return oldValue;
    });