2012-10-24 13 views
52

Rozważ to,nie może czekać asynchroniczny lambda

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

The task.Wait call() nie czeka na realizację zadań i następnej linii jest wykonywana natychmiast, ale jeśli mogę zawinąć wyrażenia lambda do asynch wywołanie metody, kod działa zgodnie z oczekiwaniami.

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

następnie (zaktualizowane zgodnie komentarz od svick)

await AwaitableMethod(); 
+0

w katalogu 'AwaitableMethod' jesteś rzeczywiście powrocie i nazywając oczekiwania na zadanie wrócił z .Delay() (zakładam, że zwraca 'zadanie'). W asynchronicznej wartości lambda, którą wywołujesz, czekaj w "Zadaniu zadania". Ale wciąż nie mam żadnego wyjaśnienia. –

+1

Powinieneś być bardzo ostrożny przy mieszaniu 'await' z' Czekaj() '. W wielu przypadkach może to prowadzić do zakleszczeń. – svick

+0

@svick znalazł świetny [przykład] (http://stackoverflow.com/a/11179035/815938) o mieszaniu 'await' z' Wait() ' – kennyzx

Odpowiedz

69

W przykładzie lambda, kiedy zadzwonić task.Wait(), czekasz na nowe zadanie, które zbudowane, a nie opóźnienie Zadanie że zwraca . Aby uzyskać żądane opóźnienie, trzeba by też czekać na wynikającym zadanie:

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

Można uniknąć budowy nowego zadania, a tylko jedno zadanie do czynienia z zamiast dwóch:

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

Bardzo polecam lekturę [Potencjalne pułapki, aby uniknąć, gdy mijają asynchroniczne lambdy ] (http://blogs.msdn.com/b/pfxteam/archive/2012/02/08/10265476.aspx) i [Task.Run kontra Task.Factory.StartNew] (http://blogs.msdn.com /b/pfxteam/archive/2011/10/24/10229468.aspx). – Andrew

+1

Jeśli pierwsze oczekiwanie następuje po dużej ilości przetwarzania, nadal możesz chcieć wykonać podwójne zadania. Zamiast 'task.Result.Wait()' można również wykonać 'task.Unwrap() .Zaczekaj()' (lub 'Unwrap ()' dla metod bezwolnych). Nowe metody 'Task.Run' są automatycznie rozwijane, więc czeka się tylko na oczekiwane zadanie. –

+4

Jako początkujący, mam wrażenie, że mogliby wykonać lepszą pracę ze słowem kluczowym 'async'; to jest bardzo mylące. – drowa

6

Musisz użyć TaskEx.RunEx.

Natywnie obsługuje metody async na TaskPool, czekając wewnętrznie na wewnętrzne zadanie. W przeciwnym razie napotkasz problem, przed którym stoisz, gdzie czeka tylko zewnętrzne zadanie, które oczywiście kończy się natychmiast, pozostawiając zadanie, które nadal wymaga oczekiwania, lub w twoim przypadku (a nawet gorzej) nieważne lambda, które nie może być oczekiwany.

Możesz też dwa razy poczekać na zadanie, aby poprawnie zbudować swoje zewnętrzne zadanie (którego aktualnie nie masz).

Aktualny kod (stałe):

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

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

Korzystanie TaskEx.RunEx:

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

await task; 
+0

Ładne wyjaśnienie, ale kod TaskEx.Run nie działa, nadal ma ten sam problem. – kennyzx

+2

Argh, przepraszam! Używam .NET 4.5 ... Chciałem napisać TaskEx.RunEx. Porównaj jego sygnaturę do TaskEx.Run - zobaczysz, dlaczego jest przeznaczony do uruchamiania metod asynchronicznych. –