C# の foreach
ループとラムダ式のスレッド セーフティの問題
次のコード スニペットを分析してみましょう:
<code class="language-csharp">foreach (Foo f in ListOfFoo) { Thread thread = new Thread(() => f.DoSomething()); threads.Add(thread); thread.Start(); }</code>
<code class="language-csharp">foreach (Foo f in ListOfFoo) { Foo f2 = f; Thread thread = new Thread(() => f2.DoSomething()); threads.Add(thread); thread.Start(); }</code>
各スレッドがスレッドの作成時と同じループ反復で Foo
インスタンスのメソッドを呼び出すことを保証するコード スニペットはどれですか?
C# 5 以降では、クロージャ スコープ内の変数定義に対するコンパイラの変更により、両方のコード スニペットが安全です。ただし、C# 5 より前では、2 番目のスニペットのみが安全でした。
最初のコード スニペットでは、変数 f
がループの外で宣言され、実行中ずっと表示されたままになります。これは、クロージャースコープ内に f
のインスタンスが 1 つだけあることを意味します。したがって、f
にアクセスする異なるスレッドが競合し、メソッド呼び出しエラーが発生する可能性があります。
この問題を軽減するために、2 番目のコード スニペットはループ内で新しい変数 f2
を宣言します。これにより、各クロージャ スコープが Foo
インスタンスへの参照の独自のコピーを持つことを保証し、スレッドごとに安全なメソッドの実行を保証します。
次の例は、この問題を示しています。
<code class="language-csharp">static void Main() { int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; foreach (int i in data) { new Thread(() => Console.WriteLine(i)).Start(); } Console.ReadLine(); }</code>
一時変数が使用されていない場合、このコードは予測できない不正確な出力を生成します。 これは、すべてのスレッドが同じ i
変数を参照し、foreach
の値が i
ループ終了後の最後の値であるためです。
したがって、スレッドの安全性を確保するには、2 番目のコード スニペットに示すように、foreach
ループ内で Lambda 式を使用するときに常にローカル変数のコピーを作成することが最善です。
以上が私の `foreach` ループと Lambda スレッドは C# で安全ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。