2012-12-28 257 views
21

我測試了async,我發現這個情況,我無法理解Task.Delay不`噸的工作:爲什麼在這種情況下

var watch = Stopwatch.StartNew(); 

var t1 = Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

var t2 = Task.Factory.StartNew(() => 
{ 
    Task.Delay(1000); 

    return 1; 
}); 

await Task.WhenAll(t1, t2); 

var result = watch.ElapsedMilliseconds; 

我想知道爲什麼結果總是0 !爲什麼不是1000,2000或兩項任務的總和3000?爲什麼Task.WhenAll等待完成任務?

+0

@GrantThomas我想分配給一個變量,但它給了我一個編譯器錯誤,我該怎麼做? – MuriloKunze

+0

@MuriloKunze爲了回答這個問題,我們需要看到代碼行和錯誤。 – Servy

+0

我試過這個:var taskresult = await Task.WhenAll(t1,t2); 但它給了我一個'不能分配void的隱式類型局部變量'。 – MuriloKunze

回答

41

好的,所以,第二個是簡單的,所以讓我們來處理那個。

對於第二項任務t2,您不會對Task.Delay(1000)的結果做任何事情。你不是await它,你不是Wait它等等。鑑於方法不是async我想你的意思是它是一個阻塞等待。要做到這一點,您希望將Wait()添加到Delay調用的末尾,以使其成爲阻止等待,或者僅使用Thread.Sleep()


對於第一個任務,你被一個事實,即你使用var咬傷。這是更清晰的發生了什麼,當你不使用var

Task<Task<int>> t1 = Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

你回不只是intTaskint任務的任務。內部任務完成後,外部任務將「完成」。當您使用WhenAll時,您不關心外部任務何時完成,您關心何時完成任務內部任務完成。有很多方法可以解決這個問題。一種是使用Unwrap

Task<int> t1 = Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}).Unwrap(); 

現在,你有你的預期Task<int>WhenAll將需要至少2000毫秒,符合市場預期。你也可以添加在另一個await調用做同樣的事情:

Task<int> t1 = await Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

正如svick in a comment提到的另一種選擇是隻用Task.Run,而不是StartNewTask.Run具有該採取Func<Task<T>>並返回Task<T>,並自動解開他們爲你的方法一組特殊的重載:

Task<int> t1 = Task.Run(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

出於這個原因,它是最好使用Task.Run爲默認選項,當你創建異步lambda表達式因爲它會爲您「處理」這個問題,但最好在無法使用Task.Run的複雜案例中瞭解它。


最後我們來看看你沒有做的選擇,這是你在這種情況下應該實際做的。由於Task.Delay已經返回一個任務,因此無需首先將其放入StartNew。而不是創建一個嵌套的任務和使用Unwrap你只是沒有在第一時間把它包:

var t3 = Task.Delay(3000); 
await Task.WhenAll(t1, t2, t3); 

如果你真的只是想等待這是你應該做的事情的固定時間量。

+0

+1爲偉大的答案 –

+0

偉大的答案:) – MuriloKunze

+4

還有另一種選擇:使用'Task.Run()'。它自己解開任務,所以它可能比使用'StartNew()'和'Unwrap()'結合起來更好。 – svick

相關問題