2012-10-24 56 views
52

考慮這一點,不能等待異步拉姆達

Task task = new Task (async() =>{ 
    await TaskEx.Delay(1000); 
}); 
task.Start(); 
task.Wait(); 

調用task.Wait()不會等待任務完成,並在下一行立即執行,但如果我換了異步lambda表達式成方法調用,代碼按預期工作。

private static async Task AwaitableMethod() 
{ 
    await TaskEx.Delay(1000);  
} 

然後

await AwaitableMethod(); 
+0

在'AwaitableMethod'你實際上是返回,並呼籲等待任務從.Delay返回()方法(我假設它返回一個'Task')。在異步lambda中,你在'Task task'上調用Wait。但是,我仍然沒有解釋。 –

+1

你應該非常小心地把'await'和'Wait()'混合在一起。在很多情況下,這可能導致死鎖。 – svick

+0

@svick發現一個很棒的[示例](http://stackoverflow.com/a/11179035/815938)關於混合'await'與'Wait()' – kennyzx

回答

69

在你的拉姆達例如,當你調用task.Wait(),你正在等待新的任務,你構建的(更新根據來自svick評論),而不是延遲任務,它返回。爲了讓您所需的延遲,你還需要等待所產生的任務:

Task<Task> task = new Task<Task>(async() => { 
    await Task.Delay(1000); 
}); 
task.Start(); 
task.Wait(); 
task.Result.Wait(); 

你可以避開構建一個新的任務,只是有一個任務來處理,而不是兩個:

Func<Task> task = async() => { 
    await TaskEx.Delay(1000); 
}; 
task().Wait(); 
+8

我強烈推薦閱讀[潛在的陷阱,以避免傳遞異步lambdas ](http://blogs.msdn.com/b/pfxteam/archive/2012/02/08/10265476.aspx)和[Task.Run vs Task.Factory.StartNew](http://blogs.msdn.com /b/pfxteam/archive/2011/10/24/10229468.aspx)。 – Andrew

+1

如果第一次等待是經過大量處理後,您可能仍然需要雙重任務。而不是'task.Result.Wait()',你也可以執行'task.Unwrap()。Wait()'(或'Unwrap ()'爲非void方法)。新的'Task.Run'方法自動解包,所以你只能等待預期的任務。 –

+4

作爲初學者,我覺得他們可以用'async'關鍵字做得更好;這很混亂。 – drowa

6

您需要使用TaskEx.RunEx

它本身支持通過在內部等待內部任務在TaskPool上運行async方法。否則,你會遇到你面臨的問題,只有外部任務正在等待,這顯然是立即完成,留下一個仍然需要等待的任務,或者在你的情況下(甚至更糟)一個無法使用的void lambda期待已久的。

或者,您可以等待任務兩次,爲您提供正確構建外部任務(目前您不是)。

當前代碼(固定):

Task task = new Task<Task>(async() =>{ 
    await TaskEx.Delay(1000); 
}); 

task.Start(); 
var innerTask = await task; 
await innerTask; 

使用TaskEx.RunEx:

Task task = TaskEx.RunEx(async() =>{ // Framework awaits your lambda internally. 
    await TaskEx.Delay(1000); 
}); 

await task; 
+0

很好的解釋,但代碼TaskEx.Run不起作用,仍然有同樣的問題。 – kennyzx

+2

啊,對不起!我正在使用.NET 4.5 ...我打算編寫TaskEx.RunEx。將其簽名與TaskEx.Run進行比較 - 您會看到爲什麼它專門用於運行異步方法。 –