2012-06-19 178 views
5

可能重複:
C# Captured Variable In Loop簡單線程

我的工作線程的幾個簡單的應用程序,但我似乎無法得到這個工作:

class ThreadTest 
{ 
    static Queue<Thread> threadQueue = new Queue<Thread>(); 

    static void Main() 
    { 
     //Create and enqueue threads 
     for (int x = 0; x < 2; x++) 
     { 
      threadQueue.Enqueue(new Thread(() => WriteNumber(x))); 
     } 

     while(threadQueue.Count != 0) 
     { 
      Thread temp = threadQueue.Dequeue(); 
      temp.Start(); 
     } 

     Console.Read(); 
    } 

    static void WriteNumber(int number) 
    { 
     for (int i = 0; i < 1000; i++) 
     { 
      Console.Write(number); 
     } 
    } 
} 

目標主要是將線程逐個添加到隊列中,然後逐個穿過隊列並彈出一個線程並執行它。因爲我的for循環中有「x < 2」,所以它應該只創建兩個線程 - 一個運行WriteNumber(0),另一個運行WriteNumber(1),這意味着我最終應該有1000個0,我的屏幕上的1000 1以不同的順序依賴於最終執行線程的方式。

我最終得到的是2000 2。我提出的兩種可能的解決方案是:我錯過了一些顯而易見的東西,或者將變量x發送到WriteNumber函數是做一個傳遞引用而不是傳值,以便當線程執行它們使用的是最新版本的x,而不是當前設置函數的時間。然而,我的理解是,變量在C#中默認通過值傳遞,並且只有在參數中包含'ref'時才通過引用傳遞。

+2

線程隊列當然是一個不尋常的想法。 – CodesInChaos

+0

這是s threadpool在內部做什麼不是一個unsuaul想法。所以使用線程池instad。 – DarthVader

+0

線程池通常有一個任務類實例的隊列,如果他們可能會被打擾追蹤,則會有一個固定的線程列表,這些線程可以將這些任務移出並執行。在列表/隊列中微觀管理線程通常會導致災難。我已經在SO上看到了一些嘗試 - 代碼中的大塊代碼嘗試調查列表中的線程是否已「完成」,釋放它們,並將新的線程填入陣列中的空洞。真的很可怕... –

回答

18

您在lambda表達式中捕獲x。在開始線程之前,x的值更改爲2。你需要做的價值副本內循環:

for (int x = 0; x < 2; x++) 
{ 
    int copy = x; 
    threadQueue.Enqueue(new Thread(() => WriteNumber(copy))); 
} 

Lambda表達式捕捉變量。儘管價值傳遞給WriteNumber按值傳遞的,它不叫所有,直到線程啓動 - 由時間x爲2

通過內環路進行復印時,每次循環得到它自己的copy變量的單獨「實例」和不會更改值...因此在調用WriteNumber時,每個copy變量仍具有與該迭代相同的值x

+0

迷人,謝謝!看起來我對lambda表達式有更多研究要做! – Jake

9

發生這種情況是因爲x的值被訪問循環結束後,到那時爲2

您需要使用臨時變量來防止變量捕獲。

for (int x = 0; x < 2; x++) 
{ 
    int tmp = x; 
    threadQueue.Enqueue(new Thread(() => WriteNumber(tmp))); 
}