网站开发的方法有哪些,外贸营销推广公司,网站开发前端和后端技术,怎样创办网站根据 .NET 官方文档的定义#xff1a;ConcurrentDictionaryTKey,TValue Class 表示可由多个线程同时访问的线程安全的键/值对集合。这也是我们在并发任务中比较常用的一个类型#xff0c;但它真的是绝对线程安全的吗#xff1f;仔细阅读官方文档#xff0c;我们会发… 根据 .NET 官方文档的定义ConcurrentDictionaryTKey,TValue Class 表示可由多个线程同时访问的线程安全的键/值对集合。这也是我们在并发任务中比较常用的一个类型但它真的是绝对线程安全的吗仔细阅读官方文档我们会发现在文档的底部线程安全性小节里这样描述ConcurrentDictionaryTKey,TValue 的所有公共和受保护的成员都是线程安全的可从多个线程并发使用。但是通过一个由 ConcurrentDictionaryTKey,TValue 实现的接口的成员包括扩展方法访问时不保证其线程安全性并且可能需要由调用方进行同步。也就是说调用 ConcurrentDictionary 本身的方法和属性可以保证都是线程安全的。但是由于 ConcurrentDictionary 实现了一些接口例如 ICollection、IEnumerable 和 IDictionary 等使用这些接口的成员或者这些接口的扩展方法不能保证其线程安全性。System.Linq.Enumerable.ToList 方法就是其中的一个例子该方法是 IEnumerable 的一个扩展方法在 ConcurrentDictionary 实例上使用该方法当它被其它线程改变时可能抛出 System.ArgumentException 异常。下面是一个简单的示例static void Main(string[] args)
{var cd new ConcurrentDictionaryint, 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.ToList 扩展方法发生异常是因为扩展方法 ToList 中调用了 List 的构造函数该构造函数接收一个 IEnumerableT 类型的参数且该构造函数中有一个对 ICollectionT 的优化由 ConcurrentDictionary 实现的。System.Collections.Generic.ListT 构造函数在 List 的构造函数中首先通过调用 Count 获取字典的大小然后以该大小初始化数组最后调用 CopyTo 将所有 KeyValuePair 项从字典复制到该数组。因为字典是可以由多个线程改变的在调用 Count 后且调用 CopyTo 前字典的大小可以增加或者减少。当 ConcurrentDictionary 试图访问数组超出其边界时将引发 ArgumentException 异常。ConcurrentDictionaryTKey,TValue 中实现的 ICollection.CopyTo 方法如果您只需要一个包含字典所有项的单独集合可以通过调用 ConcurrentDictionary.ToArray 方法来避免此异常。它完成类似的操作但是操作之前先获取了字典的所有内部锁保证了线程安全性。注意不要将此方法与 System.Linq.Enumerable.ToArray 扩展方法混淆调用 Enumerable.ToArray 像 Enumerable.ToList 一样可能引发 System.ArgumentException 异常。看下面的代码中static void Main(string[] args)
{var cd new ConcurrentDictionaryint, 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.ToArray而不是调用 Enumerable.ToArray因为后者是一个扩展方法前者重载解析的优先级高于后者。所以这段代码不会抛出异常。但是如果通过字典实现的接口继承自 IEnumerable使用字典将会调用 Enumerable.ToArray 方法并抛出异常。例如下面的代码显式地将 ConcurrentDictionary 实例分配给一个 IDictionary 变量static void Main(string[] args)
{System.Collections.Generic.IDictionaryint, int cd new ConcurrentDictionaryint, 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 异常}
}
此时调用 Enumerable.ToArray就像调用 Enumerable.ToList 时一样引发了 System.ArgumentException 异常。总结正如官方文档上所说的那样ConcurrentDictionary 的所有公共和受保护的成员都是线程安全的可从多个线程并发调用。但是通过一个由 ConcurrentDictionary 实现的接口的成员包括扩展方法访问时并不是线程安全的此时要特别注意。如果需要一个包含字典所有项的单独集合可以通过调用 ConcurrentDictionary.ToArray 方法得到千万不能使用扩展方法 ToList因为它不是线程安全的。参考http://blog.i3arnon.com/2018/01/16/concurrent-dictionary-tolist/ ConcurrentDictionary Is Not Always Thread-Safe https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2 ConcurrentDictionaryTKey,TValue Class作者 技术译民出品 技术译站https://ITTranslator.cn/END