考虑以下字典
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而不是列表来修复它。
但是,我试图了解为什么当字典本身是线程安全的时,字典的项必须是线程安全的吗?添加或更新项目时并发字典是否已经锁定?
答案 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(...);