排行榜 统计
  • 文章总数:665 篇
  • 评论总数:966 条
  • 分类总数:4 个
  • 最后更新:3月12日
none

c# 多线程并发-C# 中 ConcurrentDictionary 一定线程安全吗?

本文阅读 5 分钟
首页 正文
本文最后更新于2022年12月06日,已超过113天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

您阅读这篇文章共耗时:

  (给DotNet加星标,提升.Net技能)

  转自:技术译民.cn/dotnet/csharp

  前言

  根据 .NET 官方文档的定义: Class 表示可由多个线程同时访问的线程安全的键/值对集合。

  这也是我们在并发任务中比较常用的一个类型,但它真的是绝对线程安全的吗?

  仔细阅读官方文档,我们会发现在文档的底部线程安全性小节里这样描述:

   的所有公共和受保护的成员都是线程安全的,可从多个线程并发使用。但是,通过一个由 实现的接口的成员(包括扩展方法)访问时,不保证其线程安全性,并且可能需要由调用方进行同步。

  也就是说,调用 本身的方法和属性可以保证都是线程安全的。但是由于 实现了一些接口(例如 、 和 等),使用这些接口的成员(或者这些接口的扩展方法)不能保证其线程安全性。System.Linq..ToList 方法就是其中的一个例子,该方法是 的一个扩展方法,在 实例上使用该方法,当它被其它线程改变时可能抛出 System. 异常。下面是一个简单的示例:

  

static void Main(string[] args)    {        var cd = new ConcurrentDictionary();        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..ToList 扩展方法:

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

  System...List 构造函数:

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

   中实现的 .CopyTo 方法:

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

  注意,不要将此方法与 System.Linq.. 扩展方法混淆,调用 . 像 .ToList 一样,可能引发 System. 异常。

  看下面的代码中:

  

static void Main(string[] args)    {        var cd = new ConcurrentDictionary();        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.        }    }

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

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

  

static void Main(string[] args)    {        System.Collections.Generic.IDictionary cd = new ConcurrentDictionary();        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 异常        }    }

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

  总结

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

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

  - EOF -

本文来自投稿,不代表本站立场,如若转载,请注明出处:https://xuan.ddwoo.top/index.php/archives/456/
-- 展开阅读全文 --
c#面试题抽象类和接口的区别-最新数数网笔试题和面试题答案
« 上一篇 12-06
预装恶意软件-教你如何用cmd命令清除流氓软件
下一篇 » 12-06
------本页内容已结束,喜欢请分享------

感谢您的来访,获取更多精彩文章请收藏本站。

发表评论

本站已加入互联网信息服务许可,请规范您的言行哦~

成为第一个评论的人

作者信息

热门文章

珍惜时间哦~

今日一言

- -
加载中...
换一句

今日天气

标签TAG

热评文章