Die asynchrone Programmierung "wächst" durch die Codebasis. Es wurde mit einem Zombie-Virus verglichen . Die beste Lösung ist, es wachsen zu lassen, aber manchmal ist das nicht möglich.
Ich habe einige Typen in meine Nito.AsyncEx- Bibliothek geschrieben, um mit einer teilweise asynchronen Codebasis umzugehen . Es gibt jedoch keine Lösung, die in jeder Situation funktioniert.
Lösung A.
Wenn Sie eine einfache asynchrone Methode haben, die nicht wieder mit ihrem Kontext synchronisiert werden muss, können Sie Folgendes verwenden Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
Sie möchten nicht verwenden Task.Waitoder Task.Resultweil sie Ausnahmen einschließen AggregateException.
Diese Lösung ist nur geeignet, wenn MyAsyncMethodsie nicht mit ihrem Kontext synchronisiert wird. Mit anderen Worten, jedes awaitIn MyAsyncMethodsollte mit enden ConfigureAwait(false). Dies bedeutet, dass keine UI-Elemente aktualisiert oder auf den ASP.NET-Anforderungskontext zugegriffen werden kann.
Lösung B.
Wenn MyAsyncMethodeine Synchronisierung mit dem Kontext erforderlich ist, können Sie möglicherweise AsyncContext.RunTaskeinen verschachtelten Kontext bereitstellen:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* Update 14.04.2014: In neueren Versionen der Bibliothek lautet die API wie folgt:
var result = AsyncContext.Run(MyAsyncMethod);
( Task.ResultIn diesem Beispiel ist es in Ordnung, da RunTaskes sich ausbreitetTask Ausnahmen).
Der Grund, den Sie möglicherweise AsyncContext.RunTaskanstelle von benötigen, Task.WaitAndUnwrapExceptionist eine ziemlich subtile Deadlock-Möglichkeit, die unter WinForms / WPF / SL / ASP.NET auftritt:
- Eine synchrone Methode ruft eine asynchrone Methode auf und erhält a
Task.
- Die synchrone Methode wartet blockierend auf die
Task.
- Die
asyncMethode verwendetawait ohne ConfigureAwait.
- Das
Taskkann in dieser Situation nicht abgeschlossen werden, da es erst abgeschlossen wird, wenn die asyncMethode abgeschlossen ist. Die asyncMethode kann nicht abgeschlossen werden, da versucht wird, die Fortsetzung auf die zu planenSynchronizationContext , und WinForms / WPF / SL / ASP.NET lässt die Fortsetzung nicht zu, da die synchrone Methode bereits in diesem Kontext ausgeführt wird.
Dies ist ein Grund, warum es eine gute Idee ist, so viel wie möglich ConfigureAwait(false)in jeder asyncMethode zu verwenden .
Lösung C.
AsyncContext.RunTaskfunktioniert nicht in jedem Szenario. Wenn die asyncMethode beispielsweise auf etwas wartet, für dessen Abschluss ein UI-Ereignis erforderlich ist, blockieren Sie auch mit dem verschachtelten Kontext. In diesem Fall können Sie die asyncMethode im Thread-Pool starten :
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
Diese Lösung erfordert jedoch eine MyAsyncMethod, die im Thread-Pool-Kontext funktioniert. Daher kann es keine UI-Elemente aktualisieren oder auf den ASP.NET-Anforderungskontext zugreifen. Und in diesem Fall können Sie auch hinzufügen , ConfigureAwait(false)um seine awaitAussagen und Lösung A. verwenden
Update, 01.05.2019: Die aktuellen "am wenigsten schlimmsten Praktiken" finden Sie in einem MSDN-Artikel hier .