锁定HashSet并发 [英] Locking HashSet for concurrency
问题描述
使用 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屋!