Ich habe kürzlich Code gelesen, der viele asynchrone Methoden verwendet, aber manchmal muss er synchron ausgeführt werden. Der Code macht:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
Ist das das Gleiche wie
Foo foo = GetFooAsync(...).Result;
?
Ziemlich viel. Ein kleiner Unterschied ist jedoch: Wenn Task
fehlschlägt, wird GetResult()
die direkt verursachte Ausnahme auslösen, während Task.Result
einen AggregateException
auslöst. Welchen Sinn hat es jedoch, beide zu verwenden, wenn es async
ist? Die 100x bessere Option ist die Verwendung von await
.
Außerdem sollten Sie GetResult()
nicht verwenden. Es ist nur für Compiler gedacht, nicht für Sie. Wenn Sie jedoch nicht das ärgerliche AggregateException
möchten, verwenden Sie es.
Task.GetAwaiter().GetResult()
wird gegenüber Task.Wait
und Task.Result
bevorzugt, da es Ausnahmen weitergibt, anstatt sie in eine AggregateException
zu packen. Alle drei Methoden können jedoch zu Deadlock-Problemen führen und sollten zu Gunsten von async/await
vermieden werden.
Das folgende Zitat erklärt, warum Task.Wait
und Task.Result
nicht einfach das Ausnahmebreitungsverhalten von Task.GetAwaiter().GetResult()
enthalten (aufgrund einer "sehr hohen Kompatibilitätsleiste").
Wie ich bereits erwähnt habe, haben wir eine sehr hohe Kompatibilitätsleiste, und so haben wir das Brechen von Änderungen vermieden. Als solches behält
Task.Wait
sein ursprüngliches Verhalten bei, immer zu verpacken. Es kann jedoch vorkommen, dass Sie sich in einigen fortgeschrittenen Situationen befinden, in denen Sie ein ähnliches Verhalten wie die vonTask.Wait
verwendete synchrone Blockierung wünschen, die ursprüngliche Ausnahme jedoch nicht in eineAggregateException
eingeschlossen werden soll. Um dies zu erreichen, können Sie den Waititer der Task direkt ansprechen. Wenn Sie "await task;
" schreiben, übersetzt der Compiler dies in die Verwendung derTask.GetAwaiter()
-Methode, die eine Instanz mit einerGetResult()
-Methode zurückgibt. Bei einer fehlerhaften Aufgabe verbreitetGetResult()
die ursprüngliche Ausnahme (so erhält "await task;
" sein Verhalten). Sie können also "task.GetAwaiter().GetResult()
" verwenden, wenn Sie diese Ausbreitungslogik direkt aufrufen möchten.
https://blogs.msdn.Microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/
"
GetResult
" bedeutet eigentlich "Task auf Fehler prüfen"Im Allgemeinen versuche ich mein Bestes, um zu vermeiden, dass eine asynchrone Task synchron blockiert wird. Es gibt jedoch einige Situationen, in denen ich gegen diese Richtlinie verstoße. In diesen seltenen Fällen ist meine bevorzugte Methode
GetAwaiter().GetResult()
, da die Task-Ausnahmen beibehalten werden, anstatt sie in eineAggregateException
zu packen.
http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html
https://github.com/aspnet/Security/issues/59
"Eine letzte Bemerkung: Sie sollten
Task.Result
undTask.Wait
als Möglichst nicht verwenden, da sie die innere Ausnahme immer in einerAggregateException
kapseln und die Nachricht durch eine generische ersetzen (ein oder Weitere Fehler aufgetreten). , was das Debuggen schwieriger macht. Selbst wenn die synchrone Version nicht so oft verwendet werden sollte, sollten Sie stattdessenTask.GetAwaiter().GetResult()
in Betracht ziehen. "
Ein weiterer Unterschied besteht darin, dass die async
-Funktion nur Task
anstelle von Task<T>
zurückgibt und nicht verwendet werden kann
GetFooAsync(...).Result;
Wohingegen
GetFooAsync(...).GetAwaiter().GetResult();
funktioniert noch.
Ich weiß, dass der Beispielcode in der Frage für den Fall Task<T>
gilt, die Frage wird jedoch allgemein gestellt.
Wenn ein Task fehlerhaft ist, wird die Ausnahme erneut ausgelöst, wenn der Fortsetzungscode awaiter.GetResult () aufruft. Anstatt GetResult aufzurufen, könnten wir einfach auf die Result-Eigenschaft der Task zugreifen. Der Vorteil des Aufrufs von GetResult besteht darin, dass bei einem Fehler der Task die Ausnahme direkt ausgelöst wird, ohne in AggregateException eingeschlossen zu werden, was einfachere und sauberere catch-Blöcke ermöglicht.
Für nicht generische Tasks hat GetResult () einen ungültigen Rückgabewert. Seine nützliche Funktion besteht dann ausschließlich darin, Ausnahmen erneut zu werfen.
quelle: C # 7.0 in Kürze
Wie bereits erwähnt, wenn Sie await
verwenden können. Wenn Sie den Code synchron ausführen müssen, wie Sie .GetAwaiter().GetResult()
sagen, ist .Result
oder .Wait()
ein Risiko für Deadlocks, wie viele in Kommentaren/Antworten gesagt haben. Da die meisten von uns Oneliner mögen, können Sie diese für .Net 4.5<
verwenden.
Erfassen eines Wertes über eine asynchrone Methode:
var result = Task.Run(() => asyncGetValue()).Result;
Synchrones Aufrufen einer asynchronen Methode
Task.Run(() => asyncMethod()).Wait();
Aufgrund der Verwendung von Task.Run
treten keine Deadlock-Probleme auf.
Quelle: