web-dev-qa-db-ja.com

総称辞書に追加すると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)
   at Rpc.<MapIntoRpc>b__4[T](Object x) in Rpc.cs:line 113
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()

同じキーを複数回削除または追加しようとすると、同時実行の問題が発生する可能性があることを理解していますが、アルゴリズム的に説明しました。

追加が失敗する原因は何ですか?それを回避する最良の方法は何ですか?

30
Austin Harris

あなたはドキュメントを見ていたはずです。それが言うこと:

コレクションが変更されない限り、Dictionaryは複数のリーダーを同時にサポートできます。そうであっても、コレクションを列挙することは本質的にスレッドセーフな手順ではありません。列挙が書き込みアクセスと競合するまれなケースでは、列挙全体の間、コレクションをロックする必要があります。複数のスレッドがコレクションにアクセスして読み書きできるようにするには、独自の同期を実装する必要があります。スレッドセーフな代替方法については、ConcurrentDictionaryを参照してください。

47
Dzienny

問題はおそらく同期です。ディクショナリが追加されると、基になる構造(配列)のサイズを増やす必要がある場合があります。複数のスレッドから追加する場合は、IndexOutOfRangeExceptionになる可能性があります。安全な方法で追加していることを確認するには、ロックなどを使用する必要があります。

あるいは、スレッドセーフなコレクションである ConcurrentDictionary を使用できます。

20
NominSim

だからあなたは考えるかもしれませんWhatever! it will just break the one time-しかし、いいえ:

重要:辞書が壊れると、壊れます!

デバッグの目的で追加された辞書からは読み取られなかったため、(IISリサイクルされるまで)3時間の販売が行われます。

enter image description here

注:これは、この状態になる前に3.5年間実行されていました。

 private Dictionary<string, string> _debugLookup;

 _debugLookup[key] = virtualPath;

これは静的な辞書でもなく、インスタンスメソッドであるMVC IViewLocationCacheでした。

16
Simon_Weaver