2011-02-08 68 views
1

類似的問題被問到here,但答案一般都與lambda表示法有關。我得到沒有拉姆達類似的結果,所以我認爲我會問一些澄清:線程啓動的競態條件

說我有這樣的事情:

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。

再次,把Thread.Sleep(100)的作品,但它是從各個方面的可怕的解決方案。 你們對這種特殊類型的比賽狀況有何建議?

+0

見http://stackoverflow.com/questions/1930133/c-closures-why-is-the-loopvariable-captured-by-reference和http://stackoverflow.com/問題/ 1923577 /不同行爲時,什麼時候開始線程參數化線程啓動vs匿名/ – nos 2011-02-08 23:31:41

回答

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已經增加了大部分。


對於第二個例子,你需要做同樣的事情。只是讓臨時工:

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 2011-02-08 23:40:21

3

你的問題是一樣的;它不是lambda語法本身,而是通過匿名方法關閉局部變量(您使用的delegate語法是匿名方法的第一次迭代,並在.NET 2.0中首次亮相)。

如果你想做到這一點,你必須使用一種解決方法:

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

注意,這類似於你嘗試過什麼(複製),但它需要以外的關閉和裏面的循環。在匿名函數中複製它(例如在你的例子中)並沒有幫助。