2011-02-08 14 views
1

同様の質問がhereと尋ねられましたが、一般的にすべての回答がラムダ表記に関連しているようです。私は、私はいくつかの明確化を求めるだろうと思ってラムダせずに同様の結果を得る:スレッドスタートアップの競合条件

は、私がこのような何かを持っていると言う:

for (int i = 0; i < 5; i++) 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + i); 
    }))).Start(); 

一つは、次の出力期待:

Thread 0 
Thread 1 
Thread 2 
Thread 3 
Thread 4 

今、私はスレッドが特定の順序で開始されていないことを認識していますので、上記の行が任意の順序で出てくると仮定しましょう。

しかし、それはどういうことではありません。

Thread 3 
Thread 4 
Thread 4 
Thread 4 
Thread 4 

または類似のものではなく、私は、それが参照を渡している場合は、値を渡すよりも、それを信じるために私を導く:代わりに何が起こる 。 (int型は値型なので変です)。このような何か行う

for (int i = 0; i < 5; i++) 
    (new Thread(new ThreadStart(delegate() 
    { 
     int j = i; 
     Console.WriteLine("Thread " + j); 
    }))).Start(); 

は私達が私のコピーを行っているにもかかわらず、いずれか助けていません。私はその理由が、私が時間の中で私のコピーを作っていないと仮定しています。このような

こう何か:

for (int i = 0; i < 5; i++) 
{ 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + i); 
    }))).Start(); 
    Thread.Sleep(50); 
} 

たちは各反復では50msを無駄にしているようしかし、それは事実に言及していない、非常に望ましくない、問題を解決するようコンピュータが重く、その後にロードされた場合にはたぶん50msで十分ではないかもしれません。ここで

は私の現在、特定の問題のサンプルです:

Thread t = new Thread(new ThreadStart(delgate() 
    { 
    threadLogic(param1, param2, param3, param4); 
    })); 
t.Start(); 

param1 = param2 = param3 = param4 = null; 

で:

void threadLogic(object param1, object param2, object param3, object param4) 
{ 
    // Do some stuff here... 
} 

私はthreadLogic()は独自のスレッドで実行したい、しかし、上記のコードは、NULLを与えます参照例外。これは、スレッドが起動する機会が来る前に値がnullに設定されているためと考えられます。

もう一度、Thread.Sleep(100)を入れても、それはあらゆる面から非常に難解です。 この種の競合状態にはどんな人がおすすめですか?

+0

はhttp://stackoverflow.com/questions/1930133/c-closures-why-is-the-loopvariable-captured-by-referenceを参照してください、とhttp://stackoverflow.com/質問/ 1923577/differenting-behavior-starting-a-thread-parameterizedthreadstart-vs-anonymous/ – nos

答えて

3

あなたは一時的に導入する必要があります。

for (int i = 0; i < 5; i++) 
{ 
    int temp = i; // Add this 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + temp); 
    }))).Start(); 
} 

問題は、外側の変数(あなたのコードでi、鉱山でtemp)の周りに近い方法の代表です。スコープが間違っている(forループの外側にある)ので、スレッドが開始されるまでには、iは、すべてではないにしても、ほとんど増分されています。


2番目の例では、同じことをする必要があります。ただ、一時を行います

var temp1 = param1; 
var temp2 = param2; 
var temp3 = param3; 
var temp4 = param4; 
Thread t = new Thread(new ThreadStart(delgate() 
    { 
    threadLogic(temp1, temp2, temp3, temp4); 
    })); 
t.Start(); 

// This is now safe, since the closure above is over "temp*" 
param1 = param2 = param3 = param4 = null; 
+0

ありがとう、それは私が与えた簡単な例の素晴らしい解決策です。 t.Start()の直後に、スレッドパラメータがnullに設定されているという別の問題はどうですか? – Ozzah

+0

@Ozzah:もう一度、パラメータを一時的にコピーするだけです。こうすると、一時変数(実際の変数ではなく)が閉じられ、適切な値が得られます。 –

+0

@Ozzah:ケース2を回避する方法を示すために私の答えを編集しました。 –

3

問題は同じです。ラムダの構文自体ではなく、匿名のメソッドでローカル変数をクローズしています(delegate構文は、.NET 2.0でデビューした匿名メソッドの最初の反復です)。

あなたはこれをしたい場合、あなたは回避策を使用しています:

for (int i = 0; i < 5; i++) 
{ 
    int j = i; 
    (new Thread(new ThreadStart(delegate() 
    { 
     Console.WriteLine("Thread " + j); 
    }))).Start(); 
} 

注これは、あなたが(コピー)しようとしたものと同様であるが、それはの外する必要があることをクロージャとの内側ループ。あなたの例のような無名関数内でそれをコピーすることは役に立ちません。

関連する問題