5

次のコードはスレッドセーフですか?

var dict = new Dictionary<int, string>()
    { { 0, "" }, { 1, "" }, { 2, "" }, { 3, "" } };

var nums = dict.Keys.ToList();

Parallel.ForEach(nums, num =>
            {
                dict[num] = LongTaskToGenerateString();
            });

return dict;
4

3 に答える 3

5

いいえ、ドキュメントDictionary<TKey, TValue>に見られるように、クラスは変更に対してスレッドセーフではありません:

Dictionary<TKey, TValue>コレクションが変更されない限り、 は複数のリーダーを同時にサポートできます。それでも、コレクションの列挙は本質的にスレッドセーフな手順ではありません。列挙が書き込みアクセスと競合するまれなケースでは、列挙全体の間、コレクションをロックする必要があります。読み取りおよび書き込みのために複数のスレッドがコレクションにアクセスできるようにするには、独自の同期を実装する必要があります。

あなたの場合、一部のスレッドがLongTaskToGenerateStringほぼ同時に終了すると、辞書の更新が干渉します。

コメントでasawyerが示唆しているように、プロパティを使用しSyncRootてアクセスを手動で同期するか、単にConcurrentDictionary<TKey, TValue>クラスを取得することができます。

この実装は、既存のキーの値のみを更新している場合は問題ないことを示唆しています( thisも見てください) - 非エレガントな効果は、プロパティの不正確な値になる可能性があります。列挙中にコレクションが変更されないように保護するために使用されるため、最終的にどの値になるかは問題ではありません。ただし、これに関する保証についてはわかりません。version

于 2013-10-30T15:13:02.983 に答える
4

辞書は戻り値としてのみ使用されるように見え、実際にキーを検索するために使用されることはありません。その場合、すべての最終出力値を計算するまで辞書は必要ありません。

したがって、これには PLINQ を使用できます。

var nums = new[] { 0, 1, 2, 3 };

var dict = nums.AsParallel()
               .Select(num => new KeyValuePair<int, string>
                                  (num, LongTaskToGenerateString(num)));
               .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

return dict;
于 2013-10-30T15:30:17.247 に答える
0

スレッドセーフにするには、ConcurrentDictionaryを使用します

于 2013-10-30T15:14:19.210 に答える