锁定HashSet并发 [英] Locking HashSet for concurrency

查看:197
本文介绍了锁定HashSet并发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 HashSet< string> 进行检查时,是否曾处理过某项(即仅 Add 包含)。而且,当Contains返回false时,即使它是在...之前添加的,也没有关系。

When using a HashSet<string> to check, whether an item was handled before (i.e. only Add and Contains is used). Furthermore it is not relevant, when Contains returns false, even though it was added before ...

我遇到了以下未锁定的异常:

I encountered following exception without locking:


[IndexOutOfRangeException:索引超出数组的范围。]
System.Collections.Generic.HashSet`1.AddIfNotPresent(T value)+6108128

[IndexOutOfRangeException: Index was outside the bounds of the array.] System.Collections.Generic.HashSet`1.AddIfNotPresent(T value) +6108128

仅锁定添加调用就足够了吗?

Is it sufficient, to lock only the Add call?

以下似乎永远有效-但这不是证明...

Following seems to work forever - but that is not a proof...

HashSet<string> hashSet = new HashSet<string>();
Parallel.ForEach(GetString(), h => 
{
    hashSet.Contains(h);
    lock(hashSetLock) 
    {
        hashSet.Add(h); 
    }
    hashSet.Contains(h);
});

准确地说:我知道调用包含不带锁。我的问题是(接受错误肯定)以上代码是否可能引发异常或破坏基础数据结构(= HashSet)的内部状态。

To make it precise: I know that it is not thread-safe to call Contains without a lock. My question is (accepting false positives) if the above code could throw an exception or could destroy the internal state of the underlying data structure (=HashSet).

推荐答案

否,仅锁定 Add 是不够的。

事实它不会崩溃只是告诉您它在测试期间没有崩溃。

The fact that it doesn't crash only tells you that it didn't crash during your test.

您不能保证:


  • 将来不会崩溃

  • 它将产生正确的结果

非线程安全的数据结构不能保证以多线程方式使用。

A non-threadsafe data structure has no guarantees whatsoever if used in a multithreaded fashion.

您需要:


  • 每次调用时锁定

  • 使用线程安全数据结构,该结构已构建为支持此方案

如果您使用的数据结构与哈希集不同,例如字典,则可能甚至需要锁定多语句,因为可能仍然失败:

If you use a different data structure than a hashset, like a dictionary, you may even need to lock multi-statements, because this may still fail:

lock (dLock)
    if (d.ContainsKey("test"))
        return;

var value = ExpensiveCallToObtainValue();
lock (dLock)
    d.Add("test", value);

ContainsKey 的调用与调用之间添加另一个线程可能已经插入了该键。

Between the call to ContainsKey and the call to Add another thread may have already inserted that key.

要正确处理此问题,而无需使用线程安全的数据结构,将两个操作都包含在同一锁内:

To handle this correctly, without using a threadsafe data structure, is contain both operations inside the same lock:

lock (dLock)
{
    if (!d.ContainsKey("test"))
        d.Add("test", ExpensiveCallToObtainValue());
}

这篇关于锁定HashSet并发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆