锁定字典对象的正确方法

Correct way to lock the dictionary object

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

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 on dictionary

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

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

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

但后来我搜索到使用 SyncRoot 不是一个好的做法

经过进一步搜索,我发现有一个 ConcurrentDictionary 用于此目的

  1. 那么谁能建议我锁定字典的最佳方法
  2. 如果我使用 ConcurrentDictionary,我还需要在上面使用 lock 还是它会自行处理所有事情。
  3. 如果我必须在 ConcurrentDictionary 上使用锁定,我必须直接在其上使用 lock 或者我必须再次锁定 SyncRoot 对象

提前致谢!

使用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. So can anybody please suggest me which is the best way to lock the dictionary

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

private static object _sybcRoot = new object();

public static void Add( string key, string res)
    lock( _sybcRoot ) {
       ConnectionList.Add( key, res );
    }
}

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

  1. If I use ConcurrentDictionary do I still need to use lock on it or will it handle everything by itself.

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

  1. If I have to use lock on ConcurrentDictionary, I have to use lock on it directly or again I have to lock the SyncRoot object for it

So can anybody please suggest me which is the best way to lock the dictionary?

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

MSDN Gets an object that can be used to synchronize access to the ICollection. 因此,要实现这一点,您可以这样做

If I use ConcurrentDictionary do I still need to use lock on it or will it handle everything by itself.

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

If I have to use lock on ConcurrentDictionary, I have to use lock on it directly or again I have to lock the SyncRoot object for it

是的,如果您想在使用 GetOrAddAddOrUpdate 方法

时执行原子方法,则必须在 SyncRoot 上使用 lock