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 异常。下面是一个简单的示例:

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.ToList 扩展方法:

发生异常是因为扩展方法 ToList 中调用了 List 的构造函数,该构造函数接收一个 IEnumerable<T> 类型的参数,且该构造函数中有一个对 ICollection<T> 的优化(由 ConcurrentDictionary 实现的)。

System.Collections.Generic.List<T> 构造函数:

在 List 的构造函数中,首先通过调用 Count 获取字典的大小,然后以该大小初始化数组,最后调用 CopyTo 将所有 KeyValuePair 项从字典复制到该数组。因为字典是可以由多个线程改变的,在调用 Count 后且调用 CopyTo 前,字典的大小可以增加或者减少。当 ConcurrentDictionary 试图访问数组超出其边界时,将引发 ArgumentException 异常。

ConcurrentDictionary<TKey,TValue> 中实现的 ICollection.CopyTo 方法:


如果您只需要一个包含字典所有项的单独集合,可以通过调用 ConcurrentDictionary.ToArray 方法来避免此异常。它完成类似的操作,但是操作之前先获取了字典的所有内部锁,保证了线程安全性。

注意,不要将此方法与 System.Linq.Enumerable.ToArray 扩展方法混淆,调用 Enumerable.ToArray 像 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.ToArray(); //ConcurrentDictionary.ToArray, OK.}
}

此时调用 ConcurrentDictionary.ToArray,而不是调用 Enumerable.ToArray,因为后者是一个扩展方法,前者重载解析的优先级高于后者。所以这段代码不会抛出异常。

但是,如果通过字典实现的接口(继承自 IEnumerable)使用字典,将会调用 Enumerable.ToArray 方法并抛出异常。例如,下面的代码显式地将 ConcurrentDictionary 实例分配给一个 IDictionary 变量:

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 异常}
}

此时调用 Enumerable.ToArray,就像调用 Enumerable.ToList 时一样,引发了 System.ArgumentException 异常。

总结

正如官方文档上所说的那样,ConcurrentDictionary 的所有公共和受保护的成员都是线程安全的,可从多个线程并发调用。但是,通过一个由 ConcurrentDictionary 实现的接口的成员(包括扩展方法)访问时,并不是线程安全的,此时要特别注意。

如果需要一个包含字典所有项的单独集合,可以通过调用 ConcurrentDictionary.ToArray 方法得到,千万不能使用扩展方法 ToList,因为它不是线程安全的。


参考:

  1. http://blog.i3arnon.com/2018/01/16/concurrent-dictionary-tolist/  ConcurrentDictionary Is Not Always Thread-Safe 

  2. https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2  ConcurrentDictionary<TKey,TValue> Class

作者 :技术译民
出品 :技术译站(https://ITTranslator.cn/)

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306202.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2020 .NET 开发者峰会顺利在苏州落幕,相关数据很喜人以及线上直播回看汇总

在2019年上海中国.NET开发者大会的基础上&#xff0c;2020年12月19-20日 继续以“开源、共享、创新” 为主题的第二届中国 .NET 开发者峰会&#xff08;.NET Conf China 2020&#xff09;在苏州人工智能智能产业创新中心落下帷幕&#xff0c;本次大会以线下城市苏州为中心&…

.NET 云原生架构师训练营(模块二 基础巩固 REST RESTful)--学习笔记

2.3.1 Web API -- REST && RESTful什么是 REST&#xff0c;什么是 RESTfulRESTful API 设计RESTful 成熟度模型什么是 REST&#xff0c;什么是 RESTful理解RESTful架构&#xff1a;https://www.ruanyifeng.com/blog/2011/09/restful.htmlREST&#xff08;Representatio…

vue 一个组件内多个弹窗_论如何用Vue实现一个弹窗-一个简单的组件实现

前言最近在使用element-ui框架&#xff0c;用到了Dialog对话框组件&#xff0c;大致实现的效果&#xff0c;跟我之前自己在移动端项目里面弄的一个弹窗组件差不太多。然后就想着把这种弹窗组件的实现方式与大家分享一下&#xff0c;下面本文会带着大家手摸手实现一个弹窗组件。…

为 CefSharp 应用内置 C++ 运行环境并启用 AnyCPU 支持

一个 CefSharp 应用程序要想正确运行&#xff0c;有两个必要条件&#xff1a;.NET Framework 4.5.2VC 2015在部署 CefSharp 应用时经常会遇到因为没有 VC 2015 而无法运行的问题&#xff1a;通过事件查看器&#xff0c;可以观察到一个类型为&#xff1a;System.IO.FileNotFound…

java file rename 失败_java重命名文件造成文件不可读写

我想使用java代码对nginx日志文件进行拆分&#xff0c;但是我发现代码执行之后&#xff0c;拆分出来的日志文件没有读写权限&#xff0c;查看文件属性&#xff0c;显示的很诡异&#xff1a;点击高级按钮&#xff0c;显示你没有权限查看或者编辑这个对象的权限设置&#xff1a;反…

java opencsv_用opencsv文件读写CSV文件

首先明白csv文件长啥样儿&#xff1a;用excel打开就变成表格了&#xff0c;看不到细节推荐用其它简单粗暴一点儿的编辑器&#xff0c;比如Notepad&#xff0c;csv文件内容如下&#xff1a;csv文件默认用逗号分隔各列。有了基础的了解就进入主题&#xff0c;用Opencsv读写csv文件…

Beetlex之tcp/tls服务压测工具

在编写tcp服务的时候经常需要对服务的基础性能进行一个压力测试&#xff0c;虽然网上这些工具有很多&#xff0c;但具备使用方便和高强度的测试工具则不多。为了方便这方面的高强度压测所以在beetlex的基础扩展这样一个工具。安装可以访问https://github.com/beetlex-io/TCPBen…

GraphQL:DataLoader的神奇

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述&#xff0c;使得客户端能够准确地获得它需要的数据&#xff0c;而且没有任何冗余&#xff0c;也让 API 更容易地随着时间推移而演进&#xff0c…

【Azure Show】|第九期 “我的计算机入门之路” 嘉宾秦婷婷汪宇杰文轩

欢迎来到Azure Show!Azure Show欢迎来到Azure Show 第九期&#xff01;继上期【搭上AI快车】为大家带来数位微软技术专家于各自IT技术领域的经验分享&#xff0c;有2400多人在线上通过b站看了这个特辑的直播&#xff0c;非常非常感恩&#xff01;本期继续和广州图书馆合作&…

调试实战 | 通过转储文件分析程序无响应之使用 windbg + IDA 逆向篇

缘起 最近&#xff0c;接连在项目中遇到了两个界面无响应的问题。都只发生在客户特定机器上&#xff0c;不方便直接调试&#xff0c;只能抓取 dump 进行事后分析了。抓取 dump 远程连上可以重现问题的机器&#xff0c;使用 process explorer 初步观察卡死的进程&#xff0c;发现…

跟我一起学Redis之高可用从主从复制开始

前言现在遇到高并发场景时&#xff0c;缓存技术应该算是性能优化的第一步&#xff0c;缓解数据库压力的同时还能提高访问效率&#xff0c;而Redis应该是绝大多数应用场景的首选。但是尽快Redis性能再优秀&#xff0c;在当今高并发场景下&#xff0c;一台服务器负责读写&#xf…

【.Net core】EFCore——Code First生成数据库与表

Code First——领域设计模式中非常有用。使用 Code First 模式&#xff0c;专注于领域设计&#xff0c;创建领域类&#xff0c;然后生成数据库。1.创建数据模型类一般就是数据库里面有哪些表&#xff0c;就创建哪些模型&#xff0c; POCO 类就够了。public partial class SmsPu…

龙芯.NET正式发布 开源共享与开发者共成长

2020年12月19日&#xff0c;2020中国. NET开发者大会于苏州盛大开幕。本次大会以“开源、共享、创新”为主题&#xff0c;以线下城市苏州为中心&#xff0c;覆盖北京、上海、深圳、广州、长沙、成都、厦门、胶东等地区&#xff0c;是中国 .NET 开发者的大聚会&#xff0c;线上线…

划入 .NET 6版本目标,微软鼓励开发人员信任第三方库

喜欢就关注我们吧&#xff01;鉴于许多 .NET 社区的开发人员都不愿意使用非微软官方打造的库&#xff0c;近日&#xff0c;微软 .NET Framework 团队的项目经理 Immo Landwerth 发布了一个名为”Growing the .NET ecosystem“的文档&#xff0c;以鼓励 .NET 开发人员加强对第三…

归并排序 java 迭代_经典排序算法之归并排序(示例代码)

归并排序(英语&#xff1a;Merge sort&#xff0c;或mergesort)&#xff0c;是创建在归并操作上的一种有效的排序算法&#xff0c;效率为(大O符号)。1945年由约翰冯诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用&#xff0c;且各层分治递归可以…

新鲜高频笔面试题分享,Redis、MongoDB、ElasticSearch...

2020年可能是最短的一年&#xff0c;上班没几个月就发现要元旦了&#xff1b;2020年可能是最难的一年&#xff0c;各种降薪被离职风波&#xff1b;然而好消息是&#xff0c;2021年马上来了&#xff0c;跳槽季也要来了&#xff0c;一起开始备战吧&#xff0c;新年薪资up up&…

在 Windows 服务中托管 ASP.NET Core

概述众所周知&#xff0c;ASP.NET Core采用了和传统ASP.NET不同的托管和HTTP处理方式&#xff0c;即把服务器和托管环境完全解耦。ASP.NET Core内置了两个HTTP服务器实现&#xff0c;一个是基于libuv实现的Kestrel&#xff08;支持跨平台&#xff09;&#xff0c;一个是基于Win…

目录 | 数据结构与剑指Offer系列推文合集

【目录合集】| 作者 / Edison ZhouC#刷数据结构剑指Offer不知不觉&#xff0c;C#刷剑指Offer的系列推文就结束了&#xff0c;今天就把它们整理成目录合集。此外&#xff0c;考虑到剑指Offer和数据结构的关联&#xff0c;建议没有数据结构基础的童鞋先看看数据结构&#xff0c;再…

Abp vNext异常处理的缺陷/改造方案

之前吐槽Abp的用户/租户管理模块&#xff01;今天我又来了&#xff0c;这次我给Abp官方repo提了一个issue。目前Website使用Abp vNext开发&#xff0c;免不了要全局处理异常、提示服务器异常信息。1. Abp官方异常处理Abp项目默认会启动内置的异常处理&#xff0c;默认不将异常信…

开源项目葫芦藤:IdentityServer4的实现及其运用

前言本篇文章主要是讲解葫芦藤项目中对IdentityServer的实践使用&#xff0c;为了使您对本篇文章中所讲述的内容有深刻的认识&#xff0c;并且在阅读时避免感到乏味&#xff0c;文中的内容不会涉及太多的基础理论知识&#xff0c;而更多的是采用动手实践的方式进行讲解&#xf…