正确的方法来锁定字典对象

时间:2015-04-10 09:19:17

标签: c# multithreading dictionary locking syncroot

在我的代码中,我有一个静态字典对象

private static IDictionary< ConnKey, DbConnection > ConnectionList = new Dictionary< ConnKey, DbConnection >( );

抛出此错误

System.IndexOutOfRangeException: Index was outside the bounds of the array.
  at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
  at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)

我搜索并发现这是因为多个线程尝试访问字典,但我在字典上有lock

lock( ConnectionList ) {
   ConnectionList.Add( key, res );
}

然后我搜索了更多,发现字典上的锁定并不会阻止对它的所有操作,所以我应该在lock对象SyncRoot上使用它来实现我想要的

lock( ((IDictionary)ConnectionList).SyncRoot) {

但后来我搜索到使用SyncRoot并不是一个好习惯

在进一步搜索中,我发现有ConcurrentDictionary用于此目的

  1. 所以有人可以建议我哪种方法可以锁定字典
  2. 如果我使用ConcurrentDictionary,我仍然需要使用lock,否则它会自行处理所有内容。
  3. 如果我必须在ConcurrentDictionary上使用锁定,我必须直接使用lock,或者我必须再次锁定SyncRoot对象
  4. 提前致谢!

3 个答案:

答案 0 :(得分:15)

使用Dictionary<,>,你必须锁定读写。所以两者

lock( ConnectionList ) {
   ConnectionList.Add( key, res );
}

lock( ConnectionList ) {
   res = ConnectionList[ key ];
}

lock( ConnectionList ) {
   int cnt = ConnectionList.Count;
}

lock( ConnectionList ) {
   ConnectionList.Clear();
}

lock( ConnectionList ) {
   foreach ( var kv in ConnectionList ) {
      // Do things
   }
}

依此类推: - )

使用ConcurrentDictionary<,>您不需要任何锁定,但请注意语法与Dictionary<,>

略有不同

答案 1 :(得分:3)

  

那么有人可以建议我锁定字典的最佳方法是什么?

如果您想继续使用经典Dictionary<,> AFAK,您必须查看由Dictionary实现的ICollection界面并使用属性ICollection.SyncRoot 根据定义

<强> MSDN    Gets an object that can be used to synchronize access to the ICollection. 所以为了达到这个目的,你可以做这样的事情

  

如果我使用ConcurrentDictionary,我仍然需要使用锁定,否则它将自己处理所有内容。

来自MSDN
ConcurrentDictionary专为多线程场景而设计。您不必在代码中使用锁来添加或删除集合中的项。但是,一个线程总是可以检索一个值,另一个线程可以通过为相同的键提供一个新值来立即更新集合。

  

如果我必须在ConcurrentDictionary上使用锁,我必须直接使用锁或者再次使用它来锁定它的SyncRoot对象

是的,如果您希望在使用lockSyncRoot方法时执行Atomic方法,则必须在GetOrAdd上使用AddOrUpdate

答案 2 :(得分:2)

  
      
  1. 所以有人可以建议我哪种方法可以锁定字典
  2.   

您可以使用它SyncRoot或创建您在访问字典对象时锁定的私有对象,例如

private static object _sybcRoot = new object();

public static void Add( string key, string res)
    lock( _sybcRoot ) {
       ConnectionList.Add( key, res );
    }
}
  
      
  1. 如果我使用ConcurrentDictionary,我仍然需要使用锁定,否则它会自行处理所有内容。
  2.   

不,使用任何Concurrent*集合时无需锁定。它在设计上是线程安全的,但是这种语法略有不同。 Concurrent*集合使用无锁方法,在没有许多线程竞争访问(乐观并发)的情况下,这种方法更好

  
      
  1. 如果我必须在ConcurrentDictionary上使用锁,我必须直接使用锁或者再次使用它来锁定它的SyncRoot对象
  2.   

您必须使用相同的锁定对象来保护对相同资源的访问。否则线程可能“认为”资源是免费的,而实际上它被其他线程使用,恰好将其锁定在另一个对象的同步根上。