マルチスレッド C# の foreach ループとクロージャを理解する
C# のマルチスレッドでは、foreach
ループとクロージャーを使用するときに複雑さが生じる可能性があります。 この問題は、foreach
ループ内のクロージャがループの反復変数を参照する場合に発生します。
次の例を考えてみましょう:
<code class="language-csharp">var threads = new List<Thread>(); foreach (Foo f in ListOfFoo) { Thread thread = new Thread(() => f.DoSomething()); threads.Add(thread); thread.Start(); }</code>
ここで、各スレッドのクロージャは単一の f
変数への参照をキャプチャします。 ループが進行するにつれて、f
の値が変化し、予期しない結果が生じます。スレッドが予期しない Foo
オブジェクトを操作する可能性があります。
スレッドの安全性の確保
これを回避するには、各反復内で新しいローカル変数を作成します。
<code class="language-csharp">var threads = new List<Thread>(); foreach (Foo f in ListOfFoo) { Foo f2 = f; // Create a copy for each thread Thread thread = new Thread(() => f2.DoSomething()); threads.Add(thread); thread.Start(); }</code>
各スレッドには独自の独立した参照 (f2
) があり、DoSomething()
が正しい Foo
インスタンスで呼び出されることが保証されます。
C# 5 以降
C# 5 以降では動作が若干変更されました。 コンパイラーは、foreach
変数がループのスコープ外にある場合でも、ループのスコープ内で事実上宣言されているものとして扱います。 したがって、上記の両方のコード スニペットは、C# 5 以降のバージョンではスレッド セーフです。 ただし、明示的なコピーは、明確さと下位互換性のために依然として良い習慣です。
以上がマルチスレッド C# コードで Foreach ループとクロージャに特別な処理が必要なのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。