2009-09-14 64 views
1

我剛開始玩線程今天,我遇到了一些我不明白的東西。從循環啓動線程並傳遞循環ID

public void Main() 
{ 
    int maxValue = 5; 
    for (int ID = 0; ID < maxValue; ID++) 
    { 
     temp(ID); 
    } 
} 

public void temp(int i) 
{ 
    MessageBox.Show(i.ToString()); 
} 

作爲基本,因爲它得到的正常工作,但是當我嘗試創建的每個新的線程,它只是通過了包括maxValue。請忽略這種做法有多糟糕,我只是以一種簡單的例子來這樣寫的。

public void Main() 
{ 
    int maxValue = 5; 
    for (int ID = 0; ID < maxValue; ID++) 
    { 
     threads.Add(new Thread(() => temp(myString, rowID))); 
     threads[rowID].Start(); 
    } 
} 

public void temp(string myString, int i) 
{ 
    string _myString = myString; 

    MessageBox.Show(i.ToString()); 
} 

鑑於此,我有兩個問題: 1)爲什麼犯規一個方法被調用上傳遞的ID一個新的線程? 2)如何正確編碼?

+0

首先,你有未申報的rowID變量。將這兩個事件重命名爲ID,它將工作得很好。下面的答案是OBSOLETE! – mnn 2009-09-14 20:26:13

+0

rowID是一個錯字..如果這被改爲ID它不工作喬恩答案是正確的。 – 2009-09-14 20:34:43

+0

@mnn:不會的。您對C#中捕獲的變量的工作方式有多熟悉? – 2009-09-14 20:36:17

回答

9

問題是你只有一個ID變量,並且它被捕獲。當新線程中的代碼被實際執行時,變量僅爲,讀,這通常會在主線程完成其循環後離開ID位於maxValue。在每次循環迭代中複製一份,以便每次捕獲不同的變量:

for (int ID = 0; ID < maxValue; ID++) 
{ 
    int copy = ID; 
    threads.Add(new Thread(() => temp(myString, copy))); 
    threads[rowID].Start(); 
} 

這是閉包的常見錯誤。閱讀my article comparing C# and Java closures瞭解更多信息。同樣的事情發生與foreach,順便說一句 - 這是更加令人困惑,因爲它讀取像你已經有了一個新的變量,每次:

foreach (string url in urls) 
{ 
    // Aargh, bug! Don't do this! 
    new Thread(() => Fetch(url)).Start(); 
} 

同樣,你只能用一個變量結束。你需要每個代表來捕獲一個單獨的變量,所以你再次使用一個副本:

foreach (string url in urls) 
{ 
    string urlCopy = url; 
    new Thread(() => Fetch(urlCopy)).Start(); 
} 
+0

您在DataPimp中犯了同樣的錯誤。你不需要有其他變量,只需將rowID重命名爲ID,它就可以正常工作。 – mnn 2009-09-14 20:26:59

+0

非常感謝你,林不知道我100%理解爲什麼我發佈的第一個代碼被執行,但第二個代碼沒有執行(我猜這有點合理)但我肯定會閱讀你的文章,希望能澄清事情。 – 2009-09-14 20:33:18

+1

@mnn:不會的。你將最終得到捕獲的變量問題。它可能不總是*顯示,但你肯定會有一個錯誤。啓動線程時使用捕獲的循環變量是非常容易出錯的。假設使用'rowID'只是一個錯字,因爲使用未聲明的變量是*編譯時*錯誤而不是執行時錯誤。 – 2009-09-14 20:35:41