Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 带字典的线程安全<;int,int>;在.Net中_C#_.net_Collections_Thread Safety - Fatal编程技术网

C# 带字典的线程安全<;int,int>;在.Net中

C# 带字典的线程安全<;int,int>;在.Net中,c#,.net,collections,thread-safety,C#,.net,Collections,Thread Safety,我有这个功能: static Dictionary<int, int> KeyValueDictionary = new Dictionary<int, int>(); static void IncreaseValue(int keyId, int adjustment) { if (!KeyValueDictionary.ContainsKey(keyId)) { KeyValueDictionary.Add(keyId, 0);

我有这个功能:

static Dictionary<int, int> KeyValueDictionary = new Dictionary<int, int>();
static void IncreaseValue(int keyId, int adjustment)
{
    if (!KeyValueDictionary.ContainsKey(keyId))
    {
        KeyValueDictionary.Add(keyId, 0);
    }
    KeyValueDictionary[keyId] += adjustment;
}
静态字典KeyValueDictionary=newdictionary();
静态无效增量值(int-keyId,int-adjustment)
{
if(!KeyValueDictionary.ContainsKey(keyId))
{
KeyValueDictionary.Add(keyId,0);
}
KeyValueDictionary[keyId]+=调整;
}
我本以为这不是线程安全的。然而,到目前为止,在测试过程中,我没有看到在多个线程同时调用它时出现任何异常


我的问题是:它是线程安全的还是我到目前为止一直很幸运?如果它是线程安全的,那么为什么呢?

它不是线程安全的,但不会检查,因此可能不会注意到无提示的损坏


它在很长一段时间内看起来是线程安全的,因为只有当它需要重新缓存()时,它才有可能出现异常。否则,它只会破坏数据。

到目前为止,你很幸运。它不是线程安全的

词典可以支持多个阅读器
同时,只要集合未被修改。即便如此,
通过集合枚举本质上不是线程安全的
程序在枚举与写入争用的罕见情况下
访问时,必须在整个枚举期间锁定集合。
允许多个线程访问集合以进行读取
在编写时,您必须实现自己的同步


.NET库有一个线程安全字典,
ConcurrentDictionary

更新:我没有准确地回答这个问题,所以这里更新了对提出的确切问题的更多回答。 根据MSDN:

一本词典可以同时支持多个读者, 只要集合未被修改。即便如此,列举 通过集合本质上不是线程安全的过程。在里面 枚举与写访问争用的罕见情况是 在整个枚举过程中必须锁定集合。允许 要由多个线程访问以进行读写的集合, 您必须实现自己的同步

有关线程安全的替代方案,请参阅ConcurrentDictionary

此类型的公共静态(在Visual Basic中共享)成员是线程 安全的

然而,到目前为止,在测试过程中,我没有看到在多个线程同时调用它时出现任何异常

它是线程安全的还是我到目前为止一直很幸运?如果它是线程安全的,那么为什么呢

你走运了。这些类型的线程错误很容易产生,因为测试可能会给你一种错误的安全感,让你做得正确

当您有多个编写器时,
字典
不是线程安全的。文件明确规定:

只要不修改集合,
词典
可以同时支持多个读取器。即便如此,通过集合枚举本质上并不是线程安全的过程。在枚举与写访问争用的罕见情况下,必须在整个枚举期间锁定集合要允许多个线程访问集合进行读写,您必须实现自己的同步。

或者,使用。但是,您仍然必须编写正确的代码(请参见下面的注释)

除了您幸运地避免了
字典
的线程安全性之外,您的代码还有危险的缺陷。以下是如何让代码出现bug:

static void IncreaseValue(int keyId, int adjustment) {
    if (!KeyValueDictionary.ContainsKey(keyId)) {
        // A
        KeyValueDictionary.Add(keyId, 0);
    }
    KeyValueDictionary[keyId] += adjustment;
}
  • 字典是空的
  • 线程1以
    keyId=17进入方法。由于字典是空的,
    if
    中的条件返回
    true
    ,线程1到达标记为
    A
    的代码行
  • 线程1暂停,线程2以
    keyId=17进入方法。由于字典为空,
    if
    中的条件返回
    true
    ,线程2到达标记为
    A
    的代码行
  • 线程2暂停,线程1继续。现在线程1将
    (17,0)
    添加到字典中
  • 线程1暂停,现在线程2恢复。现在线程2尝试将
    (17,0)
    添加到字典中。由于密钥冲突而引发异常
  • 还有其他可能发生异常的场景。例如,线程1可以在加载
    KeyValueDictionary[keyId]
    的值时暂停(假设它加载
    keyId=17
    ,并获取值
    42
    ),线程2可以进来修改值(假设它加载
    keyId=17
    ,添加调整
    27
    ),现在线程1恢复并将其调整添加到加载的值中(特别是,它没有看到线程2对与
    keyId=17
    !)关联的值所做的修改)

    请注意,即使使用
    ConcurrentDictionary
    也可能导致上述错误!您的代码不安全,原因与
    字典的线程安全无关或缺少线程安全

    要使用并发字典使代码具有线程安全性,您必须说:

    KeyValueDictionary.AddOrUpdate(keyId, adjustment, (key, value) => value + adjustment);
    

    我们正在使用。

    这是危险的误导。即使将OP代码中的字典替换为
    ConcurrentDictionary
    ,代码仍然是不安全的“当我们实施竞争条件时,竞争条件的典型行为是什么?它顺利通过单元测试和系统测试。我们有我们的释放派对。然后我们接到了我们最重要的客户的电话,报告了非确定性崩溃。”@FuleSnabel:lol,很好。+1:回答得好,感谢您的努力。特别喜欢您探讨了为什么“线程安全”集合仍然允许“线程不安全”的部分“代码。感谢您花时间和精力整理这个答案。我最初的解决方案在耳鼻喉科周围有一个锁