在ConcurrentDictionary中实现的dictionary成员是线程安全的吗?
本文关键字:线程 安全 dictionary ConcurrentDictionary 实现 成员 | 更新日期: 2023-09-27 18:03:10
当调用DoStuffToDictionary(dictionaryTwo)时,是否可以安全地假设方法体内的操作包括索引器和LINQ扩展方法也将是线程安全的?
换句话说,会出现跨线程异常或死锁吗?
var dictionaryOne = new ConcurrentDictionary<int,int>();
var dictionaryTwo = new Dictionary<int,int>();
DoStuffToDictionary(dictionaryOne);
DoStuffToDictionary(dictionaryTwo);
void DoStuffToDictionary(IDictionary<int,int> items) {
// Manipulate dictionary
if (items[0] == -1) {
items[0] = 0; // Dumb example, but are indexers like this OK?
}
}
这段代码有几个问题:
-
IDictionary
接口可以被任何类型的字典实现
您的示例当然不是线程安全的,因为您在IDictionary<int,int>
接口上工作,这不能保证任何线程安全。甚至你的代码同时传递一个Dictionary
和一个ConcurrentDictionary
给方法。 -
事务需要是原子的,以使它们线程安全
之间锁定对字典的访问。
即使字典实现保证是线程安全的,您的代码也不会,因为您没有在两个调用:if (items[0] == -1) { // <-- another thread can access items in this moment items[0] = 0; }
-
返回LINQ查询永远不是线程安全的
如果您使用LINQ从您的方法返回IEnumerable
或IQueriable
,那么锁几乎没有影响,除非您使用ToList()
方法立即计算表达式并缓存结果。这是由于LINQ只"准备"要执行的查询。如果您从一个方法返回一个IEnumerable
,实际的字典将在方法结束后(因此,在锁之外)被访问。
IDictionary
实例,这意味着代码的其他部分可以直接访问它,并且必须非常小心地锁定同一个锁对象实例。这是痛苦的,正确实现容易出错,容易意外中断,并且难以检测(竞争条件可能在极少数情况下显示症状)。
你可以做几件事来改进代码:
不要传递
IDictionary
,而是传递自己的接口(首选)
使字典成为实现一些自定义接口的类的私有成员,抽象所有操作,并使用锁来确保线程安全(或在底层使用ConcurrentDictionary
)。这样你就可以确保所有的调用都被同一个锁实例所锁定。不要使用接口,而总是传递
ConcurrentDictionary
只要使用ConcurrentDictionary
提供的特定的原子方法(GetOrAdd
,AddOrUpdate
等),这将是线程安全的。像在示例中那样使用简单的访问方法将不是线程安全的,这意味着您仍然需要小心使用它。另一个缺点是,如果需要的话,您将无法抽象功能(不可能包装/代理字典,并且您将无法使用其他字典实现)。传递
IDictionary
,并锁定字典本身(根本不推荐)。
这是一个丑陋的黑客,不幸的是,它被使用得比它应该使用的更频繁。这意味着你需要在中访问这个字典的每个部分执行此操作,并在此过程中额外注意锁定多个操作。
行
if (items[0] == -1) {
items[0] = 0; // Dumb example, but are indexers like this OK?
不是线程安全的…使用ConcurrentDictionary this
ConcurrentDictionary<int, int> D = new ConcurrentDictionary<int, int>();
D.TryUpdate(0, 0, -1); // this is threadsade regarding a ConcurrentDictionary
将是线程安全的,您希望通过上述行实现的结果。
1)对于Dictionary
LINQ扩展方法不是线程安全的,因为类本身不是线程安全的,但对于ConcurrentDictionary
它是线程安全的,因此调用LINQ
扩展函数为它是线程安全的。
2) Is it safe to assume the operations within the method body including indexers
我不知道你所说的including indexers
是什么意思,但如果你是指字典不是空的。那么对于Dictionary
,你不应该认为它包含项目…但是对于ConcurrentDictionary
,您可以使用以下方法:AddOrUpdate();, GetOrAdd();, TryAdd();, TryGetValue();