20

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

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 );
}

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

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

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

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

  1. 那么任何人都可以建议我锁定字典的最佳方法吗
  2. 如果我使用ConcurrentDictionary它,我仍然需要使用lock它还是它会自己处理所有事情。
  3. 如果我必须使用 lock on ConcurrentDictionary,我必须lock直接使用它或再次我必须SyncRoot为它锁定对象

提前致谢!

4

3 回答 3

21

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<,>

于 2015-04-10T09:21:53.113 回答
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 对象

是的,如果您想在使用 or方法 时执行原子方法,则必须使用lockonSyncRootGetOrAddAddOrUpdate

于 2015-04-10T09:39:53.957 回答
3
  1. 那么任何人都可以建议我锁定字典的最佳方法吗

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

private static object _sybcRoot = new object();

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

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

  1. 如果我使用 ConcurrentDictionary 是否仍需要对其使用锁定,或者它会自行处理所有事情。

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

  1. 如果我必须在 ConcurrentDictionary 上使用锁,我必须直接或再次使用锁,我必须为它锁定 SyncRoot 对象
于 2015-04-10T09:29:49.940 回答