.Net C# 中ConcurrentDictionary一定线程安全吗?
Posted シ゛甜虾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net C# 中ConcurrentDictionary一定线程安全吗?相关的知识,希望对你有一定的参考价值。
根据 .NET 官方文档的定义:ConcurrentDictionary<TKey,TValue>
Class 表示可由多个线程同时访问的线程安全的键/值对集合。这也是我们在并发任务中比较常用的一个类型,但它真的是绝对线程安全的吗?
仔细阅读官方文档,我们会发现在文档的底部线程安全性小节里这样描述:
ConcurrentDictionary<TKey,TValue>
的所有公共和受保护的成员都是线程安全的,可从多个线程并发使用。但是,通过一个由ConcurrentDictionary<TKey,TValue>
实现的接口的成员(包括扩展方法)访问时,不保证其线程安全性,并且可能需要由调用方进行同步。
也就是说,调用 ConcurrentDictionary 本身的方法和属性可以保证都是线程安全的。但是由于 ConcurrentDictionary 实现了一些接口(例如 ICollection、IEnumerable 和 IDictionary 等),使用这些接口的成员(或者这些接口的扩展方法)不能保证其线程安全性。System.Linq.Enumerable.ToList
方法就是其中的一个例子,该方法是 IEnumerable
的一个扩展方法,在 ConcurrentDictionary 实例上使用该方法,当它被其它线程改变时可能抛出 System.ArgumentException
异常。下面是一个简单的示例:
调用 System.Linq.Enumerable.ToList,抛出 System.ArgumentException 异常
static void Main(string[] args)
{
var cd = new ConcurrentDictionary<int, int>();
Task.Run(() =>
{
var random = new Random();
while (true)
{
var value = random.Next(10000);
cd.AddOrUpdate(value, value, (key, oldValue) => value);
}
});
while (true)
{
cd.ToList(); //调用 System.Linq.Enumerable.ToList,抛出 System.ArgumentException 异常
}
}
调用 System.Linq.Enumerable.ToArray,抛出 System.ArgumentException 异常
static void Main(string[] args)
{
System.Collections.Generic.IDictionary<int, int> cd = new ConcurrentDictionary<int, int>();
Task.Run(() =>
{
var random = new Random();
while (true)
{
var value = random.Next(10000);
cd[value] = value;
}
});
while (true)
{
cd.ToArray(); //调用 System.Linq.Enumerable.ToArray,抛出 System.ArgumentException 异常
}
}
调用 ConcurrentDictionary.ToArray, OK. 不会抛出异常
static void Main(string[] args)
{
var cd = new ConcurrentDictionary<int, int>();
Task.Run(() =>
{
var random = new Random();
while (true)
{
var value = random.Next(10000);
cd.AddOrUpdate(value, value, (key, oldValue) => value);
}
});
while (true)
{
cd.ToArray(); //ConcurrentDictionary.ToArray, OK.
}
}
正如官方文档上所说的那样,ConcurrentDictionary 的所有公共和受保护的成员都是线程安全的,可从多个线程并发调用。但是,通过一个由 ConcurrentDictionary 实现的接口的成员(包括扩展方法)访问时,并不是线程安全的,此时要特别注意。
如果需要一个包含字典所有项的单独集合,可以通过调用 ConcurrentDictionary.ToArray
方法得到,千万不能使用扩展方法 ToList
,因为它不是线程安全的。
以上是关于.Net C# 中ConcurrentDictionary一定线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章