在ConcurrentDictionary线程中实现的IDictionary成员是否安全?

时间:2011-07-24 10:37:54

标签: c# multithreading

调用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?
  }
}

3 个答案:

答案 0 :(得分:7)

此代码存在以下几个问题:

  1. IDictionary界面可以通过任何类型的字典实现
    您的示例肯定不是线程安全的,因为您正在使用IDictionary<int,int>接口,这不保证任何线程安全。即使您的代码也会将DictionaryConcurrentDictionary传递给该方法。

  2. 事务需要是原子的才能使它们成为线程安全的
    即使字典实现保证是线程安全的,您的代码也不会是因为您没有锁定对两个调用之间字典的访问

    if (items[0] == -1) {
        // <-- another thread can access items in this moment
        items[0] = 0;
    }
    
  3. 返回LINQ查询绝不是线程安全的
    如果您使用LINQ从方法返回IEnumerableIQueriable,则锁定效果不大,除非您使用ToList()方法立即计算表达式并缓存结果。这是因为LINQ仅“准备”查询以执行。如果您从某个方法返回IEnumerable,则在方法结束后将访问实际字典(因此,在锁定之外)。

  4. 此代码的最大问题在于您传递IDictionary实例,这意味着您的代码的其他部分可以直接访问它,并且必须锁定非常仔细地使用相同的锁对象实例。这很痛苦,容易出错,容易被意外打破,很难被发现(竞争条件可能会在极少数情况下出现症状)。

    您可以做几件事来改进代码:

    1. 不要传递IDictionary,而是传递您自己的界面(首选)
      使字典成为类的私有成员,该类实现一些自定义接口,抽象所有操作,并使用锁来确保线程安全(或使用引擎盖下的ConcurrentDictionary)。这样,您可以确保使用相同的锁定实例锁定所有调用。

    2. 请勿使用该界面,而应始终通过ConcurrentDictionary
      只要您使用ConcurrentDictionary提供的特定原子方法(GetOrAddAddOrUpdate等),这将是线程安全的。使用像您在示例中所做的简单访问方法将不是线程安全的,这意味着您仍然需要小心它。另外的缺点是,如果您需要,您将无法抽象功能(无法包装/代理字典,并且您将无法使用其他字典实现)。

      < / LI>
    3. 传递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