如何显示,双重检查锁图案与字典的TryGetValue不是线程 [英] How to show that the double-checked-lock pattern with Dictionary's TryGetValue is not threadsafe
问题描述
最近我已经看到了使用双检锁模式在词典
一些C#项目。事情是这样的:
Recently I've seen some C# projects that use a double-checked-lock pattern on a Dictionary
. Something like this:
private static readonly object _lock = new object();
private static volatile IDictionary<string, object> _cache =
new Dictionary<string, object>();
public static object Create(string key)
{
object val;
if (!_cache.TryGetValue(key, out val))
{
lock (_lock)
{
if (!_cache.TryGetValue(key, out val))
{
val = new object(); // factory construction based on key here.
_cache.Add(key, val);
}
}
}
return val;
}
这code是不正确的,因为在词典
可以生长在 _cache.Add集合()
,而 _cache.TryGetValue
(锁外)被遍历集合。这可能是在许多情况的可能性微乎其微,但仍然是错误的。
This code is incorrect, since the Dictionary
can be "growing" the collection in _cache.Add()
while _cache.TryGetValue
(outside the lock) is iterating over the collection. It might be extremely unlikely in many situations, but is still wrong.
有一个简单的程序来演示这个code失败?
Is there a simple program to demonstrate that this code fails?
是否有意义将其纳入一个单元测试?如果是的话,怎么办?
Does it make sense to incorporate this into a unit test? And if so, how?
推荐答案
在这个例子中,异常#1几乎是瞬间在我的机器上抛出:
In this example, exception #1 is thrown almost instantly on my machine:
var dict = new Dictionary<int, string>() { { 1234, "OK" } };
new Thread(() =>
{
for (; ; )
{
string s;
if (!dict.TryGetValue(1234, out s))
{
throw new Exception(); // #1
}
else if (s != "OK")
{
throw new Exception(); // #2
}
}
}).Start();
Thread.Sleep(1000);
Random r = new Random();
for (; ; )
{
int k;
do { k = r.Next(); } while (k == 1234);
Debug.Assert(k != 1234);
dict[k] = "FAIL";
}
不过,code,它不是设计的确切行为是线程安全的是未predictable 。
您不能依靠它。因此,仔细检查code确实是坏了。
However, the exact behaviour of code that is not designed to be thread-safe is unpredictable.
You cannot rely on it. So the double-checking code is indeed broken.
我不知道如果我的单元测试这一点,虽然,作为测试并发code(和获得它的权利)要复杂得多比写并发code摆在首位。
I'm not sure if I'd unit test this, though, as testing concurrent code (and getting it right) is much more complicated than writing the concurrent code in the first place.
这篇关于如何显示,双重检查锁图案与字典的TryGetValue不是线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!