2016-12-30 7 views
2

インターネット上のどこかで、いくつかの型のコレクションを列挙しながら置き換えることが可能であることが確認されています。

以下のテストで確認できます。C#列挙中にコレクションを置き換えます。

// This test confirmed insufficient by comments 
var a = new List<int> { 1, 2, 3 }; 

Parallel.For(1, 10000, i => { 
    foreach (var x in a) 
     Console.WriteLine(i + x); 
}); 
Parallel.For(1, 10000, i => a = new List<int> { 1, 2, 3, 4 }); 

しかし私は非常に私は私のコードでそれを実装を開始する前に、いくつかの公式ドキュメントや、この事実に関連するいくつかの具体的な言及を読みたいです。

誰かがこれを確認/リンクを投稿できますか?

+3

2つのParallel.Forが順番に実行されます。 Parallel.Forは、すべての反復が完了したときに終了します。だからあなたはそれを列挙しながらコレクションを置き換えていません。 – NineBerry

+3

テスト中に置き換えられている間、コレクションは反復されていません。 Parrallel.Forは1から1000の反復を並行して実行し、999の反復がすべて完了した場合にのみ返します。 foreachはあなたが "List a"を反復している間にあなたを完全に憎むでしょう。 – prof1990

答えて

4

すでに言及したように、反復している間は実際にaに突然変異させることはありません。あなたは束を反復していますし、束を反復処理した後、Parallel.Forはすべての反復の実行を終了するまでブロックするので、aに束縛しています。

の場合は、aの反復処理と並行して実際には完全に安全です。 foreachはでa一度の値を読み取るために起こっている非常に、スタートリストへの参照を取得して、その時点から、が、決してa再びを見に行くんです。それはaから得られたリストへの参照へのローカルコピーからうまくいっているので、それ以降は変数aに何が変更されたのか分からないでしょう。したがって、リストaが何点を指していて、同時に反復するのがaの場合、別のスレッドの変更の前または後に反復されるリストがaにあったかどうかわかりませんが、リストが反復されているでなければならず、どちらかのエラーまたは両方の組み合わせではありません。今

a参照むしろ全く異なるだろうし、新しい参照を指すように変数aを変異よりも、あなたがリストを変異された場合。 Listは同時に複数のスレッドからアクセスするようには設計されていないため、あらゆる種類の悪いことが起こります。複数のスレッドからアクセスするように特別に設計されたコレクションを使用し、使用するように設計されたコレクションを使用した場合は、正しく機能する可能性があります。

+0

私は必要な場面でコンカレント辞書を使用しています。しかし、現在のユースケースでは、リファレンスを置き換えるだけで十分です。説明をありがとう、私は自分のコードを実装する自信を持っています。 –

0

Servyの答えにコメントを付け加えれば、実際に変数を並行して変更していくうちに、その変数を反復することができます。あなたのParallel.Forループは順次実行する - すなわち、最初のあなたは(再び、おそらく並行して)新しいリストを10000回それを置き換えるそして、リストの上に10000回(おそらく並行して)を繰り返します。私はおそらく並列にを言っ

// This doesn't modify or replace the collection at all, it just iterates over it a bunch of times 
Parallel.For(1, 10000, i => { 
    foreach (var x in a) 
     Console.WriteLine(i + x); 
}); 

// This happens AFTER the previous Parallel.For loop completes 
// Thus, you're not actually iterating over the loop at this point, just replacing it a bunch of times 
Parallel.For(1, 10000, i => a = new List<int> { 1, 2, 3, 4 }); 

注 - 単にParallel.Forループで何かを置くことは、フレームワークが実際にタスクを達成するために複数のスレッドを使用することを保証するものではありません、あなたが事前に」予測することはできませんもしそれがあれば、どれくらいのスレッドを使うのでしょうか?このコードでは、これらのタスクが複数のスレッドで実行されていること(または、実行されているスレッドの数)を必ずしも証明する必要さえありません。

このテストの他の欠陥の1つ:毎回同じ正確なコレクションでクラスを置き換えているため、ループが完了した後に最終的に更新されたスレッドを特定できません。 A、B、Cの3種類のスレッドを使用して、コレクションを最後に更新したことをどのように知っていますか? Parallel.Forループはではなく、が順番に実行されることが保証されているので、3つのいずれかによって更新されている可能性があることを思い出してください。 documentation(強調鉱山)から:

並列ループの構文はのために、あなたが既に知っているループ foreachのと非常に似ていますが、並列ループが速く、使用可能なコアを持ってい コンピュータ上で実行されます。 もう1つの違いは、 シーケンシャルループとは異なり、実行順序がループ ループに対して定義されていないことです。手順は同時に、同時に並行して行われることがよくあります。 時々、 の2つのステップが、ループ が連続している場合とは逆の順序で行われることがあります。唯一の保証は、すべてのループの 反復は、ループが終了するまでに実行されることです。

は基本的に、そして、Parallel.Forループであなたはそれがすべてで並列処理を使用していますかどうか全くわから「事前に」並列度を、持っていない、あるいはどのように使用する(ステップがで実行されますご注文しますこの構造は、コードが実際にどのように実行されるかをかなり制御することを必然的に伴います)。

+2

OPは、コレクションを保持している変数に変更を加えており、反復されているコレクションを変更していないため、この状況には当てはまりません。 – Servy

+0

@Servy明確にするために編集します – EJoshuaS

+0

@Servy私の編集はこれを修正しますか?私の見解では、コレクションを変更しているかどうかは実際には関係ありません。なぜなら、実際には2番目のループのリストを反復しているわけではないからです。 – EJoshuaS

関連する問題