2011-10-23 74 views
0

簡單複印麪食這裏:.NET 4.0:請幫我解決這個問題的任務

static void Main(string[] args) 
{ 
    List<Task> Tasks = new List<Task>(); 

    Random r = new Random(); 

    for (int o = 0; o < 5; o++) 
     Tasks.Add(Task.Factory.StartNew(() => { int i = r.Next(0, 3000); Thread.Sleep(i); Console.WriteLine("{0}: {1}", o, i); })); 

    Task.WaitAll(Tasks.ToArray()); 

    Console.Read(); 
} 

當您運行,你會得到這樣的事情:

5: 98 
5: 198 
5: 658 
5: 1149 
5: 1300 

什麼我不理解對這個?當我期望以隨機順序看到數字0到4時,編寫o的每次迭代對於所有線程顯示爲5。

我嘗試使用實際的方法,而不是匿名,它做同樣的事情。我錯過了什麼?

編輯:我剛剛發現我的第一篇文章的問題,並編輯了問題,所以很抱歉,如果你回答有關不正確的訂單問題。不過,我很好奇爲什麼o寫得不好。

回答

3
() => 
{ 
    int i = r.Next(0, 3000); 
    Thread.Sleep(i); 
    Console.WriteLine("{0}: {1}", o, i); 
}) 

你是closing over your loop variableo與您用於任務委託 - 在一次執行你的循環已完成,你只得到最終值5 o。請記住,您正在通過循環變量創建閉包,而不是當前的 - 僅在任務啓動後執行委託時纔會評估該值。

你必須創建循環變量的本地副本,而不是,然後你就可以安全使用:

for (int o = 0; o < 5; o++) 
{ 
    int localO = o; 
    Tasks.Add(Task.Factory.StartNew(() => { int i = r.Next(0, 3000); Thread.Sleep(i); Console.WriteLine("{0}: {1}", localO, i); })); 
} 
+0

我是不知道我明白。你能提供代碼,以便我能看到你的意思嗎? – oscilatingcretin

+0

工作正常!我仍然不明白這個「關閉循環變量」的東西,但我只需要閱讀更多的內容。 – oscilatingcretin

+0

@ scilatingcretin:Eric Lippert的文章鏈接上面,讓人有一個好讀 – BrokenGlass

2

這裏至少有兩個問題。

o在每次迭代中的值爲5的問題是詞法關閉的那些「陷阱」之一。如果你想o捕捉其當前值,您必須在循環內創建一個本地變量和使用,在您的拉姆達,例如:

for (int o = 0; o < 5; ++o) 
{ 
    int localO = o; 
    // now use "localO" in your lambda ... 
} 

此外,Random不是線程安全的。在多個線程中同時使用同一個Random實例可能會損壞其狀態並給您帶來意想不到的結果。

1

我認爲你正在創建它們的任務是在順序執行的假設,以及TPL沒有這樣的保證...

至於「O」參數始終打印爲5,即是因爲它是匿名函數的父範圍中的局部變量,因此當打印實際執行時,它的值爲5,因爲循環已完成(與'i'在作用範圍內的匿名函數相比)

+0

我並不期望他們按照他們創建的順序執行,但顯示o以隨機順序打印爲0-4。不過,我發現我在那部分做錯了。 – oscilatingcretin

相關問題