2017-03-14 3 views
2

私はロックを持ち、スレッドを5つ作成する簡単なコードを持っています。スレッド:このコードはスレッドの出力に5を表示するのはなぜですか

static readonly object _object = new object(); 

static void A(int currentValue) 
{ 
    lock (_object) 
    { 
     Console.WriteLine(currentValue + " Start");     
     Console.WriteLine(currentValue + " End"); 
    } 
} 

static void Main(string[] args) 
{ 
    for (int i = 0; i < 5; i++) 
    { 
     Thread t = new Thread(() => A(i)); 
     t.Name = "Thread " + i; 
     Console.WriteLine(t.Name + " Created"); 
     t.Start();     
    } 
    Console.ReadKey(); 
} 

このコードを実行すると、次のように出力されます。

Thread 0 Created 
Thread 1 Created 
1 Start 
1 End 
Thread 2 Created 
2 Start 
2 End 
Thread 3 Created 
3 Start 
3 End 
Thread 4 Created 
4 Start 
4 End 
5 Start 
5 End 

私は5つのスレッド(0〜4)しか作成していません。 5 Startと5 Endのエントリが表示されるのはなぜですか?

+0

ポストインクリメントの代わりにプリインクリメントを試してください。 ++ i –

+0

根本的な原因はわかりませんが、これらは関連していると思います。あなたは「0スタート」と「0エンド」の行がないことに気付きます。各スレッドが実行される前に増分されているように見えますが、「スレッド作成済み」の行でも同じです。私の推測では、スレッドが実行されるとき、スレッド変数を宣言したときのiの値ではなく、iの最新の値をとります。あなたは一貫して同じ結果を得ていますか? (Peter Dunihoの回答も参照してください) – Jarak

+3

問題は、スレッドが開始されるまでに変数 'i'が変更されたことです。すべてのスレッドがループ全体が完了する前に開始されることさえ幸運です。通常、このようなバグを書くと、その動作は_all_のスレッドが '5'を出力することになります。それを修正する方法の詳細については、重複したもの(Lasseの回答)を参照してください。 –

答えて

0

問題は、スレッドが開始されたときにということで、Aの呼び出しは、コンパイラは、パラメータを取得するために行く、このラインに一瞬のためにジャンプしますhapppens:

Thread t = new Thread(() => A(i)); 

場合は、この時点で時間内にループが既に終了している場合、変数iの値は5になります。したがって、この値が使用されます。この現象は私が推測する閉鎖と呼ばれています。ここでは、このような新たな宣言一時変数にi値を保存し、5ここ

for (int i = 0; i < 5; i++) 
{ 
    int temp = i; 
    Thread t = new Thread(() => A(temp)); 
    t.Name = "Thread " + i; 
    t.Start(); 
} 

もうUPT来ない場合は、それから抜け出すことができJon Skeet

による記事The Beauty of Closuresanswer to a very similar problemされています

Aのローカル変数のみにアクセスするため、このコンテキストではlockは実際には必要ありません。すべてのスレッドには独自の変数があります。ロックは実際にスレッドが並列に実行されるのを抑制します。あなたがそれをこのようにしたいのでなければ、あなたはスレッドを必要としません。

関連する問題