6

a を使用して、ConcurrentDictioanry<string, HashSet<string>>多くのスレッドにまたがるデータにアクセスします。

この記事(下にスクロール) で、メソッドAddOrUpdateがロックで実行されないため、スレッド セーフが危険にさらされる可能性があることを読みました。

私のコードは次のとおりです。

//keys and bar are not the concern here
ConcurrentDictioanry<string, HashSet<string>> foo = new ...;
foreach(var key in keys) {
    foo.AddOrUpdate(key, new HashSet<string> { bar }, (key, val) => {
        val.Add(bar);
        return val;
    });
}

すべてがスレッドセーフであることを確認するために、AddOrUpdate呼び出しをステートメントで囲む必要がありますか?lock

4

4 に答える 4

8

単独でロックAddOrUpdateするだけでは役に立ちません。 set から読み取るたびにロックする必要があります。

このコレクションをスレッド セーフとして扱う場合は、値もスレッド セーフにする必要があります。理想的には、が必要ですConcurrentSet。これはフレームワーク内には存在しませんが (何かを見逃していない限り)、基礎となるデータ構造としてa (または好きなもの)ConcurrentSet<T>を使用する独自のものを作成できます。基本的に、辞書内の値を無視し、キーの存在を重要な部分として扱います。ConcurrentDictionary<T, int>TValue

すべてを実装する必要はありませんISet<T>- 実際に必要なビットだけです。

次に、アプリケーション コードで を作成するConcurrentDictionary<string, ConcurrentSet<string>>と、離れてしまいます。ロックする必要はありません。

于 2012-09-13T20:54:16.407 に答える
4

このコードを修正する必要があります。大量のガベージが作成されます。何も必要ない場合でも、新しい HashSet を作成します。valueFactoryデリゲートを受け入れるもう 1 つのオーバーロードを使用します。したがって、HashSet は、キーがまだ辞書に存在しない場合にのみ作成されます。

複数のスレッドがkeyの同じ値を同時に追加しようとして、それが存在しない場合、valueFactoryが複数回呼び出される可能性があります。非常に低いオッズですが、ゼロではありません。これらのハッシュセットの 1 つだけが使用されます。HashSet を作成しても、スレッドの問題を引き起こす可能性のある副作用はありません。余分なコピーがガベージ コレクションされるだけです。

于 2012-09-13T21:15:43.090 に答える
0

この記事では、追加デリゲートはディクショナリのロックでは実行されず、取得する要素は追加デリゲートによってそのスレッドで作成された要素ではない可能性があると述べています。これはスレッド セーフの問題ではありません。ディクショナリの状態は一貫しており、呼び出し元ごとに異なるインスタンスが作成された場合でも、すべての呼び出し元が同じインスタンスを取得します (1 つを除いてすべて破棄されます)。

于 2012-09-13T21:00:41.073 に答える