为什么ConcurrentDictionary的值需要是线程安全的?

时间:2019-02-20 21:43:19

标签: c# multithreading

考虑以下字典

var dic = new ConcurrentDictionary<Guid, List<Customer>>();

如果我执行Parallel.For并将项添加到该字典中,则结果将不可预测。有时列表中的项目为空,有时会有例外。

var id = Guid.NewGuid();
Parallel.For(0, 1000, _ =>
{
    dic.AddOrUpdate(id,new List<Customer>(), (key, lst) =>
    {
        var c = new Customer()
        {
             Id = key
        };
        lst.Add(c);
        return lst;
    });
});

我知道List.Add并不是线程安全的,因此导致了问题。我可以通过使用ConcurrentBag而不是列表来修复它。

但是,我试图了解为什么当字典本身是线程安全的时,字典的项必须是线程安全的吗?添加或更新项目时并发字典是否已经锁定?

2 个答案:

答案 0 :(得分:2)

从此处的MSDN文档(重点是我的):https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.addorupdate?view=netframework-4.7.2

  

对于字典的修改和写操作,ConcurrentDictionary使用细粒度的锁定来确保线程安全。 (对字典的读取操作以无锁的方式执行。)但是,在锁外调用 updateValueFactory委托,以避免在锁下执行未知代码可能引起的问题。因此,对于ConcurrentDictionary类上的所有其他操作,AddOrUpdate不是原子的。

答案 1 :(得分:0)

在以下情况下,ConcurrentDictionary的锁定不会为您提供帮助:

线程1:

List<Customer> c = ConcurrentDictionary[{guid}];
Thread.Sleep(1000);

线程2:

List<Customer> c2 = ConcurrentDictionary[{same guid}];
c2.Add(...);

线程1:

c.Add(...);