调用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?
}
}
答案 0 :(得分:7)
此代码存在以下几个问题:
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
,并锁定字典本身(根本不推荐)。
这是一个丑陋的黑客,不幸的是,它经常被使用。这意味着您需要在访问此词典的应用的每个部分执行此操作,并在此过程中额外注意锁定多个操作。
答案 1 :(得分:2)
线(合在一起)
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
对于您希望通过上述行实现的结果是线程安全的。
答案 2 :(得分:1)
1)对于Dictionary
LINQ扩展方法不是线程安全的,因为类本身不是线程安全的,但对于ConcurrentDictionary
它是一个线程安全的,所以调用{{1它的扩展函数是线程安全的。
修改强>
2)LINQ
我不知道Is it safe to assume the operations within the method body including indexers
你的意思,但如果你指的是字典不是空的。然后对于including indexers
,您不应该认为它包含项目..但对于Dictionary
,您可以使用以下方法:ConcurrentDictionary