34

私は以下のコードを使用しています

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));
});

ありがとう。

4

6 に答える 6

34

いいえ!安全ではないので、まったく安全でprocessed.Addはありません。次のことができます。

items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList();

これは主に、シーケンスの各要素の命令型Parallel.ForEach操作のために作成されたことを覚えておいてください。あなたがすることはマップです:シーケンスの各値を投影します。それが作成されたものです。最も効率的な方法でスレッド間でスケーリングします。SelectAsParallel

このコードは正しく機能します:

var processed = new List<Guid>();
Parallel.ForEach(items, item => 
{
    lock(items.SyncRoot)
        processed.Add(SomeProcessingFunc(item));
});

ただし、マルチスレッドに関しては意味がありません。lock各反復で強制的に完全に順次実行されると、多数のスレッドが単一のスレッドを待機します。

于 2011-02-16T18:25:35.400 に答える
8

使用する:

var processed = new ConcurrentBag<Guid>();

並列foreachループ-奇妙な動作を参照してください。

于 2011-02-16T18:27:31.733 に答える
4

JonSkeetの本C#in Depthから:

.Net4のParallelExtensionsの一部として、新しいSystem.Collections.Concurrent名前空間にいくつかの新しいコレクションがあります。これらは、比較的少ないロックで、複数のスレッドからの同時操作に直面しても安全であるように設計されています。

これらには以下が含まれます:

  • IProducerConsumerCollection<T>
  • BlockingCollection<T>
  • ConcurrentBag<T>
  • ConcurrentQueue<T>
  • ConcurrentStack<T>
  • ConcurrentDictionary<TKey, TValue>
  • その他
于 2011-02-16T18:27:14.070 に答える
1

アンドレイの答えの代わりとして:

items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList();

あなたも書くことができます

items.AsParallel().ForAll(item => SomeProcessingFunc(item));

これにより、 MSDNのマージが不要になるため、背後にあるクエリがさらに効率的になります。SomeProcessingFunc関数がスレッドセーフであることを確認してください。そして、私はそれをテストしませんでしたが、リストを他のスレッド(追加または削除)要素で変更できる場合は、まだロックが必要だと思います。

于 2012-08-07T15:43:35.087 に答える
1

タイプSomethingのConcurrentBagを使用する

var bag = new ConcurrentBag<List<Something>>;
var items = GetAllItemsINeed();
Parallel.For(items,i =>                          
   {
      bag.Add(i.DoSomethingInEachI());
   });
于 2016-01-24T17:07:26.430 に答える
0

読み取りはスレッドセーフですが、追加はそうではありません。追加すると内部アレイのサイズが変更され、同時読み取りが台無しになる可能性があるため、リーダー/ライターロックの設定が必要です。

追加時に配列のサイズが変更されないことを保証できる場合は、読み取り中に追加しても安全かもしれませんが、それについては引用しないでください。

しかし実際には、リストは配列への単なるインターフェースです。

于 2011-02-16T18:39:35.183 に答える