web-dev-qa-db-ja.com

辞書オブジェクトをロックする正しい方法

私のコードには静的辞書オブジェクトがあります

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オブジェクトをロックする必要があります

前もって感謝します!

18
Pawan Nogariya

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<,>の構文と少し異なることに注意してください。

20
xanatos

それで、誰が私に辞書をロックする最善の方法が私に提案することができますか?

従来のDictionary<,> AFAKを引き続き使用する場合は、辞書によって実装されているICollectionインターフェイスを調べ、プロパティ ICollection.SyncRoot を使用する必要があります。

[〜#〜] msdn [〜#〜]Gets an object that can be used to synchronize access to the ICollection.これを達成するにはあなたはこのようなことをすることができます

ConcurrentDictionaryを使用する場合でも、ロックを使用する必要がありますか、それともすべてを単独で処理しますか?.

From [〜#〜] msdn [〜#〜]
ConcurrentDictionaryはマルチスレッドシナリオ用に設計されています。コレクションの項目を追加または削除するためにコードでロックを使用する必要はありません。ただし、1つのスレッドが常に値を取得し、別のスレッドが同じキーに新しい値を与えることでコレクションをすぐに更新することは常に可能です。

ConcurrentDictionaryでロックを使用する必要がある場合は、直接ロックするか、もう一度SyncRootオブジェクトをロックする必要があります。

はい、lockまたはSyncRootメソッドを使用するときにアトミックメソッドを実行する場合は、GetOrAddAddOrUpdateを使用する必要があります。

3
BRAHIM Kamel
  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オブジェクトをロックする必要があります。

同じロックオブジェクトを使用して、同じリソースへのアクセスを保護する必要があります。そうでない場合、スレッドはリソースが解放されていると「考える」可能性がありますが、実際には、他のオブジェクトの同期ルートでたまたまロックする他のスレッドによって使用されています。

2
oleksii