私は以下のコードを使用しています
var processed = new List<Guid>();
Parallel.ForEach(items, item =>
{
processed.Add(SomeProcessingFunc(item));
});
上記のコードスレッドは安全ですか?処理されたリストが破損する可能性はありますか?または、追加する前にロックを使用する必要がありますか?
var processed = new List<Guid>();
Parallel.ForEach(items, item =>
{
lock(items.SyncRoot)
processed.Add(SomeProcessingFunc(item));
});
ありがとう。
番号! processed.Add
は安全ではないため、安全ではありません。次のことができます。
items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList();
Parallel.ForEach
は、主にシーケンスの各要素の命令操作のために作成されたことを覚えておいてください。あなたがすることはマップです:シーケンスの各値を投影します。そのためにSelect
が作成されました。 AsParallel
は、最も効率的な方法でスレッド間でスケーリングします。
このコードは正しく機能します:
var processed = new List<Guid>();
Parallel.ForEach(items, item =>
{
lock(items.SyncRoot)
processed.Add(SomeProcessingFunc(item));
});
ただし、マルチスレッドに関しては意味がありません。各反復でlock
ingを実行すると、完全に順次実行され、多数のスレッドが単一のスレッドを待機します。
彼がここに来る前に Jon Skeet から引用するには:
.Net4のParellelExtensionsの一部として、新しい
System.Collections.Concurrent
名前空間にいくつかの新しいコレクションがあります。これらは、比較的少ないロックで、複数のスレッドからの同時操作に直面しても安全であるように設計されています。
これらには、とりわけIProducerConsumerCollection<T>, BlockingCollection<T>, ConcurrentBag<T>, ConcurrentQueue<T>, ConcurrentStack<T>, and ConcurrentDictionary<TKey, TValue>
が含まれます。
SomethingタイプのConcurrentBagを使用
var bag = new ConcurrentBag<List<Something>>;
var items = GetAllItemsINeed();
Parallel.For(items,i =>
{
bag.Add(i.DoSomethingInEachI());
});
アンドレイの answer の代わりとして:
items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList();
あなたも書くことができます
items.AsParallel().ForAll(item => SomeProcessingFunc(item));
これにより、マージが不要なため、背後にあるクエリがさらに効率的になります [〜#〜] msdn [〜#〜] 。 SomeProcessingFunc
関数がスレッドセーフであることを確認してください。また、テストはしていませんが、リストを他のスレッド(追加または削除)要素で変更できる場合は、ロックが必要だと思います。
読み取りはスレッドセーフですが、追加はそうではありません。追加すると内部配列のサイズが変更され、同時読み取りが台無しになる可能性があるため、リーダー/ライターロックの設定が必要です。
追加時に配列のサイズが変更されないことを保証できる場合は、読み取り中に追加しても安全かもしれませんが、それについては引用しないでください。
しかし実際には、リストは配列への単なるインターフェースです。