.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,因为它不是线程安全的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花开花落的个人博客

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值