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:回答得好,感谢您的努力。特别喜欢您探讨了为什么“线程安全”集合仍然允许“线程不安全”的部分“代码。感谢您花时间和精力整理这个答案。我最初的解决方案在耳鼻喉科周围有一个锁