c# - .Net 中 Dictionary<int,int> 的线程安全

标签 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);
    }
    KeyValueDictionary[keyId] += adjustment;
}

我原以为这不是线程安全的。但是,到目前为止,在测试它时,同时从多个线程调用它时,我还没有看到任何异常。

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

最佳答案

However, so far in testing it I have not seen any exceptions when calling it from multiple threads at the same time.

Is it thread safe or have I just been lucky so far? If it is thread safe then why?

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

事实证明Dictionary<TKey, TValue>当您有多个编写器时,它不是线程安全的。文档明确指出:

A Dictionary<TKey, TValue> can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

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

此外 Dictionary<TKey, TValue> 缺乏线程安全性你很幸运地避免了,你的代码存在危险的缺陷。以下是如何让您的代码出现错误:

static void IncreaseValue(int keyId, int adjustment) {
    if (!KeyValueDictionary.ContainsKey(keyId)) {
        // A
        KeyValueDictionary.Add(keyId, 0);
    }
    KeyValueDictionary[keyId] += adjustment;
}
  1. 字典是空的。
  2. 线程 1 以 keyId = 17 进入方法.由于字典为空,if 中的条件语句返回 true线程 1 到达标记为 A 的代码行.
  3. 线程 1 暂停,线程 2 进入带有 keyId = 17 的方法.由于字典为空,if 中的条件语句返回 true线程 2 到达标记为 A 的代码行.
  4. 线程 2 暂停,线程 1 恢复。现在线程 1 添加 (17, 0)到字典。
  5. 线程 1 暂停,现在线程 2 恢复。现在线程 2 尝试添加 (17, 0)到字典。由于 key 违规引发异常。

还有其他可能发生异常的场景。例如,线程 1 可以在加载 KeyValueDictionary[keyId] 的值时暂停。 (假设它加载 keyId = 17 并获得值 42 ),线程 2 可以进入并修改该值(假设它加载 keyId = 17 ,添加调整 27 ),现在线程 1 恢复并添加其调整它加载的值(特别是,它看不到线程 2 对与 keyId = 17 关联的值所做的修改!)。

请注意,即使使用 ConcurrentDictionary<TKey, TValue>可能导致上述错误!由于与 Dictionary<TKey, TValue> 的线程安全无关或缺乏线程安全的原因,您的代码不安全.

为了让你的代码对并发字典是线程安全的,你必须说:

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

这里我们使用 ConcurrentDictionary.AddOrUpdate .

关于c# - .Net 中 Dictionary<int,int> 的线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7246005/

相关文章:

c# - 动态转换为通用接口(interface)

.net - 如果您使用需要特定安全权限的属性来装饰您的代码,会收到什么好处?

java - 比较父列表结构中的嵌套列表以获取java中特定索引中的值

.net - VB.NET 中只读集合属性的集合初始值设定项?

c# - 调用外部 JS 和 CSS 到我的网站 - 有什么风险

c# - 使用 XUnit c# 进行功能测试中的类型化配置

c# - ASP.NET WEB API 2 - ModelBinding 每个请求触发两次

c# - 如何在 C# 中进行只进、只读的 WMI 查询?

c# - 参数名称 : root cannot be Null exception Ninject

java - 取消排序 Hashtable 中的排序对象