Dictionary的线程安全在.Net中
我有这个函数:
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;
}
我认为这不是线程安全的。然而,到目前为止,在测试它时,我还没有看到同时从多个线程调用它时出现任何异常。
我的问题:它是线程安全的还是到目前为止我只是幸运?如果它是线程安全的那为什么呢?
I have this function:
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;
}
Which I would have thought would not be thread safe. However, so far in testing it I have not seen any exceptions when calling it from multiple threads at the same time.
My questions: Is it thread safe or have I just been lucky so far? If it is thread safe then why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
你很幸运。这些类型的线程错误很容易出现,因为测试会给你一种错误的安全感,让你觉得你做的事情是正确的。
事实证明,当您有多个编写器时,
Dictionary
不是线程安全的。文档明确指出:或者,使用
ConcurrentDictionary
。但是,您仍然必须编写正确的代码(请参见下面的注释)。除了您幸运地避免的
Dictionary
缺乏线程安全性之外,您的代码还存在危险的缺陷。以下是您的代码出现错误的方法:keyId = 17
的方法。由于字典为空,if
中的条件返回true
,线程 1 到达标记为A
的代码行。keyId = 17
的方法。由于字典为空,if
中的条件返回true
,线程 2 到达标记为A
的代码行。(17, 0)
添加到字典中。(17, 0)
添加到字典中。由于关键违规而引发异常。还有其他可能发生异常的情况。例如,线程 1 在加载
KeyValueDictionary[keyId]
的值时可以暂停(假设它加载keyId = 17
,并获取值42
>),线程 2 可以进来并修改值(假设它加载keyId = 17
,添加调整27
),现在线程 1 恢复并将其调整添加到它的价值已加载(特别是,它看不到线程 2 对与keyId = 17
关联的值所做的修改!)。请注意,即使使用
ConcurrentDictionary
也可能导致上述错误!由于与Dictionary
的线程安全无关或缺乏线程安全无关的原因,您的代码不安全。要使您的代码使用并发字典实现线程安全,您必须说:
这里我们使用
ConcurrentDictionary.AddOrUpdate
。You're getting lucky. These types of bugs with threads are so easy to make because testing can you give you a false sense of security that you did things correctly.
It turns out that
Dictionary<TKey, TValue>
is not thread-safe when you have multiple writers. The documentation explicitly states:Alternatively, use
ConcurrentDictionary
. However, you still must write correct code (see note below).In addition to the lack of thread-safety with
Dictionary<TKey, TValue>
which you've been lucky to avoid, your code is dangerously flawed. Here's how you can get a bug with your code:keyId = 17
. As the Dictionary is empty, the conditional in theif
returnstrue
and thread 1 reaches the line of code markedA
.keyId = 17
. As the Dictionary is empty, the conditional in theif
returnstrue
and thread 2 reaches the line of code markedA
.(17, 0)
to the dictionary.(17, 0)
to the dictionary. An exception is thrown because of a key violation.There are other scenarios in which an exception can occur. For example, thread 1 could be paused while loading the value of
KeyValueDictionary[keyId]
(say it loadskeyId = 17
, and obtains the value42
), thread 2 could come in and modify the value (say it loadskeyId = 17
, adds the adjustment27
), and now thread 1 resumes and adds its adjustment to the value it loaded (in particular, it doesn't see the modification that thread 2 made to the value associated withkeyId = 17
!).Note that even using a
ConcurrentDictionary<TKey, TValue>
could lead to the above bugs! Your code is NOT safe for reasons not related to the thread-safety or lack thereof forDictionary<TKey, TValue>
.To get your code to be thread-safe with a concurrent dictionary, you'll have to say:
Here we are using
ConcurrentDictionary.AddOrUpdate
.它不是线程安全的,但不检查,因此可能不会注意到静默损坏。
它在很长一段时间内看起来都是线程安全的,因为只有当它需要 rehash() 时,它才有可能出现异常。否则,它只会损坏数据。
It's not thread safe, but does not check and so probably doesn't notice silent corruption.
It will appear to be thread safe for a long time because only when it needs to rehash() does it have even a chance of exception. Otherwise, it just corrupts data.
.NET 库有一个线程安全字典,即
ConcurrentDictionary
http://msdn.microsoft.com/en-us/library/dd287191.aspx更新:我没有完全回答这个问题,所以这里更新了对所提出的确切问题的更多答案。
根据 MSDN:http://msdn.microsoft.com/en-us/库/xfhwa508.aspx
The .NET library has a thread safe dictionary, the
ConcurrentDictionary<TKey, TValue>
http://msdn.microsoft.com/en-us/library/dd287191.aspxUpdated: I didn't exactly answer the question, so here's updated with more answery to exact question posed.
As per the MSDN:http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
到目前为止你很幸运。它不是线程安全的。
来自
Dictionary
文档...You've just been lucky so far. It's not thread-safe.
From the
Dictionary<K,V>
documentation...