5

10 個のスレッドを作成し、メッセージをコンソールに書き込む次のコードがあります。

for (int i = 0; i < 10; i++)
{
    {
        Thread thread = new Thread((threadNumber) =>
            {
                for (int j = 0; j < 10; j++)
                {
                    Thread.Sleep(200);
                    Console.WriteLine(string.Format("Thread: {0}, Line: {1}", threadNumber, j));
                }                           
            });
        thread.Start(i);
    }
}

私の理解ではParameterizedThreadStart、参照のコピーがスレッドに送信されるオブジェクトを取得します。その場合、i各ループ内でローカル コピーを作成していないため、すべての新しいスレッドが同じメモリ位置を指すことになり、特定のスレッド番号が「見逃される」可能性があります。ただし、これを実行すると (さらに多数のスレッド/スリープ時間に対しても)、 の各値にiは独自のスレッドがあります。誰でも理由を説明できますか?

4

1 に答える 1

4

ラップする匿名関数を作成するという意味で、遅延または「キャプチャ」されたものは何も適用していませんi

ここのラムダ関数はiどこにも参照されておらず、その状態は完全に内部化/含まれているため、ここでは問題ありません。

(threadNumber) =>
{
    for (int j = 0; j < 10; j++)
    {
        Thread.Sleep(200);
        Console.WriteLine(string.Format("Thread: {0}, Line: {1}", threadNumber, j));
    }                           
});

ここでのStart呼び出し:

thread.Start(i);

これは「値型」であり、どの種類の無名関数でもキャプチャiされないため、値渡し (つまり、その値をコピー) します。この意味で、struct通常のメソッドに通常どおり渡されます (これがまさに起こっていることだからです)。


代わりに、ラムダを次のように記述した場合、 の代わりに次を使用しiますthreadNumber

{
    for (int j = 0; j < 10; j++)
    {
        Thread.Sleep(200);
        Console.WriteLine(string.Format("Thread: {0}, Line: {1}", i, j));
    }                           
});

それでは困りますね。この場合i、元の変数の場所を参照しており、スレッドが実行されるたびに評価されます。これは、それ作成されたときの現在の値(処理時間のせいではない可能性が高い)、ループ内でi後で設定された値、または最後の可能な値である可能性があり、番号がスキップされたり、反復間で共有されたりする可能性が非常に高いことを意味します。for10

于 2013-07-13T17:50:02.027 に答える