2012-10-24 6 views
52

des Betrachtennicht async Lambda

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

Der Aufruf task.Wait() nicht warten, bis die Aufgabenerledigung und die nächste Zeile wird sofort ausgeführt, erwarten aber, wenn ich von dem Asynchron-Lambda-Ausdruck in eine wickeln Methodenaufruf funktioniert der Code wie erwartet.

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

dann (aktualisiert nach Kommentar von svick)

await AwaitableMethod(); 
+0

In der 'AwaitableMethod' Sie tatsächlich zurückkehrt und Warten der Aufforderung an die Aufgabe aus der .Delay() zurück Methode (ich gehe davon aus, dass es eine 'Aufgabe' zurückgibt). In dem asynchronen Lambda, das Sie aufrufen, warten Sie auf die Task-Task. Aber trotzdem habe ich keine Erklärung. –

+1

Sie sollten sehr vorsichtig sein, wenn Sie "warten" mit "Warten()" mischen. In vielen Fällen kann dies zu Deadlocks führen. – svick

+0

@svick fand ein großartiges [Beispiel] (http://stackoverflow.com/a/11179035/815938) über das Mischen von 'erwarten' mit' Wait() ' – kennyzx

Antwort

69

In Ihrem Lambda Beispiel, wenn Sie task.Wait() nennen, die Sie auf die neue Aufgabe wartet, die Sie aufgebaut, nicht die Verzögerung Aufgabe, die es zurückgibt . Um die gewünschte Verzögerung zu erhalten, würden Sie müssen auch auf der resultierende Aufgabe warten:

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

Sie vermeiden könnte eine neue Task-Konstruktion, und haben nur eine Aufgabe zu bewältigen statt zwei:

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

Ich empfehle dringend zu lesen [Mögliche Fallstricke zu vermeiden, wenn async lambdas herumreichen ] (http://blogs.msdn.com/b/pfxteam/archive/2012/02/08/10265476.aspx) und [Task.Run vs. Task.Factory.StartNew] (http://blogs.msdn.com) /b/pfxteam/archive/2011/10/24/10229468.aspx). – Andrew

+1

Wenn die erste Wartezeit nach viel Verarbeitung ist, möchten Sie vielleicht immer noch die Doppelaufgaben. Anstatt 'task.Result.Wait()' können Sie auch 'task.Unwrap(). Wait()' (oder 'Unwrap ()' für nicht-void Methoden). Die neuen 'Task.Run'-Methoden werden automatisch entfernt, sodass Sie nur auf die erwartete Aufgabe warten müssen. –

+4

Als Anfänger habe ich den Eindruck, dass sie mit dem Schlüsselwort 'async' einen besseren Job gemacht hätten; Es ist sehr verwirrend. – drowa

6

Sie müssen TaskEx.RunEx verwenden.

Es unterstützt nativ async Methoden auf dem TaskPool, indem er intern die innere Aufgabe erwartet. Andernfalls werden Sie auf das Problem stoßen, auf das Sie stoßen, wo nur die äußere Aufgabe erwartet wird, die offensichtlich sofort beendet wird, entweder eine Aufgabe, die noch warten muss, oder in Ihrem Fall (und noch schlimmer) ein ungültiges Lambda, das nicht sein kann erwartet.

Alternativ können Sie die Aufgabe zweimal abwarten, vorausgesetzt, Sie erstellen Ihre äußere Aufgabe korrekt (was Sie zur Zeit nicht sind).

Aktueller Code (fest):

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

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

Mit TaskEx.RunEx:

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

await task; 
+0

Schöne Erklärung, aber der Code TaskEx.Run funktioniert nicht, hat immer noch das gleiche Problem. – kennyzx

+2

Argh, Entschuldigung! Ich benutze .NET 4.5 ... Ich wollte TaskEx.RunEx schreiben. Vergleichen Sie die Signatur mit TaskEx.Run - Sie werden sehen, warum sie speziell für asynchrone Methoden verwendet wird. –