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.Wait
oder Task.Result
weil sie Ausnahmen einschließen AggregateException
.
Diese Lösung ist nur geeignet, wenn MyAsyncMethod
sie nicht mit ihrem Kontext synchronisiert wird. Mit anderen Worten, jedes await
In MyAsyncMethod
sollte mit enden ConfigureAwait(false)
. Dies bedeutet, dass keine UI-Elemente aktualisiert oder auf den ASP.NET-Anforderungskontext zugegriffen werden kann.
Lösung B.
Wenn MyAsyncMethod
eine Synchronisierung mit dem Kontext erforderlich ist, können Sie möglicherweise AsyncContext.RunTask
einen 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.Result
In diesem Beispiel ist es in Ordnung, da RunTask
es sich ausbreitetTask
Ausnahmen).
Der Grund, den Sie möglicherweise AsyncContext.RunTask
anstelle von benötigen, Task.WaitAndUnwrapException
ist 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
async
Methode verwendetawait
ohne ConfigureAwait
.
- Das
Task
kann in dieser Situation nicht abgeschlossen werden, da es erst abgeschlossen wird, wenn die async
Methode abgeschlossen ist. Die async
Methode 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 async
Methode zu verwenden .
Lösung C.
AsyncContext.RunTask
funktioniert nicht in jedem Szenario. Wenn die async
Methode 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 async
Methode 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 await
Aussagen und Lösung A. verwenden
Update, 01.05.2019: Die aktuellen "am wenigsten schlimmsten Praktiken" finden Sie in einem MSDN-Artikel hier .