如何显示,双重检查锁图案与字典的TryGetValue不是线程 [英] How to show that the double-checked-lock pattern with Dictionary's TryGetValue is not threadsafe

查看:157
本文介绍了如何显示,双重检查锁图案与字典的TryGetValue不是线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我已经看到了使用双检锁模式在词典一些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屋!

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