Ist Task.Result dasselbe wie .GetAwaiter.GetResult ()?

326

Ich habe kürzlich Code gelesen, der viele asynchrone Methoden verwendet, diese aber manchmal synchron ausführen muss. Der Code macht:

Foo foo = GetFooAsync(...).GetAwaiter().GetResult();

Ist das das gleiche wie

Foo foo = GetFooAsync(...).Result;
Jay Bazuzi
quelle
8
Aus den Dokumenten von GetResult: "Dieser Typ und seine Mitglieder sind für die Verwendung durch den Compiler vorgesehen." Andere Personen sollten es nicht benutzen.
Spender
32
Dies wird als "Synchronisierung über Asynchronisierung" bezeichnet. Wenn Sie nicht wissen, wie die Aufgabe implementiert wird, kann dies eine wirklich schlechte Idee sein. In vielen Fällen kann es sofort zum asyncawait
Stillstand kommen
13
In der realen Welt haben wir Konstruktoren, wir haben "no wait" -Schnittstellen, die wir implementieren müssen, und wir erhalten überall asynchrone Methoden. Ich würde gerne etwas verwenden, das einfach funktioniert, ohne dass ich mich fragen muss, warum es "gefährlich", "nicht verwendet" oder "um jeden Preis zu vermeiden" ist. Jedes Mal, wenn ich mich mit Async herumschlagen muss, treten Kopfschmerzen auf.
Larry

Antworten:

173

Ja schon. Ein kleiner Unterschied: Wenn das Taskfehlschlägt, GetResult()wird nur die direkt verursachte Ausnahme ausgelöst, während Task.Resulteine ausgelöst wird AggregateException. Was bringt es jedoch, eines davon zu verwenden, wenn es ist async? Die 100x bessere Option ist zu verwenden await.

Du sollst es auch nicht benutzen GetResult(). Es ist nur für den Compiler gedacht, nicht für Sie. Aber wenn Sie das nervige nicht wollen AggregateException, verwenden Sie es.

Es ist nicht wahr.
quelle
27
@ JayBazuzi Nicht, wenn Ihr Unit-Testing-Framework asynchrone Unit-Tests unterstützt, was meiner Meinung nach die neuesten Versionen der meisten Frameworks tun.
Svick
15
@ JayBazuzi: MSTest, xUnit und NUnit unterstützen alle async TaskUnit-Tests und haben seit einiger Zeit.
Stephen Cleary
18
Drücken Sie das 100-fache zurück - es ist 1000-mal schlechter, das Warten zu verwenden, wenn Sie alten Code anpassen und das Warten verwenden, muss neu geschrieben werden.
stecken
13
@AlexZhukovskiy: Ich bin anderer Meinung .
Stephen Cleary
15
The 100x better option is to use await.Ich hasse awaitsolche Aussagen, wenn ich davor klatschen könnte, würde ich es tun . Aber, wenn ich versuche , wie Asynchron - Code zu arbeiten gegen Nicht-Asynchron - Code zu bekommen , was mich häufig passiert eine Menge in Xamarin, ich am Ende mit Sachen zu verwenden , wie ContinueWithviel, um es nicht zu machen , die UI Deadlock. Bearbeiten: Ich weiß, dass dies alt ist, aber das lindert nicht meine Frustration, Antworten zu finden, die dies ohne Alternativen für Situationen angeben, in denen Sie nicht einfach verwenden können await.
Thomas F.
144

Task.GetAwaiter().GetResult()wird gegenüber Task.Waitund bevorzugt, Task.Resultweil es Ausnahmen verbreitet, anstatt sie in eine zu verpacken AggregateException. Alle drei Methoden können jedoch zu Deadlock- und Thread-Pool-Hungerproblemen führen. Sie sollten alle zugunsten von vermieden werden async/await.

Das folgende Zitat erklärt, warum Task.Waitund enthält Task.Resultnicht einfach das Ausnahmeverbreitungsverhalten von Task.GetAwaiter().GetResult()(aufgrund einer "sehr hohen Kompatibilitätsleiste").

Wie ich bereits erwähnt habe, haben wir eine sehr hohe Kompatibilitätsleiste und haben daher vermieden, Änderungen zu brechen. Als solches Task.Waitbehält es sein ursprüngliches Verhalten bei, immer zu wickeln. Es kann jedoch vorkommen, dass Sie sich in einigen fortgeschrittenen Situationen befinden, in denen Sie ein Verhalten wünschen, das dem von synchronisierten Blockieren ähnlich Task.Waitist, bei dem Sie jedoch möchten, dass die ursprüngliche Ausnahme unverpackt weitergegeben wird, anstatt in eine eingeschlossen zu werden AggregateException. Um dies zu erreichen, können Sie den Kellner der Aufgabe direkt ansprechen. Wenn Sie " await task;" schreiben , übersetzt der Compiler dies in die Verwendung der Task.GetAwaiter()Methode, die eine Instanz mit einer GetResult()Methode zurückgibt . Bei Verwendung für eine fehlerhafte Task GetResult()wird die ursprüngliche Ausnahme weitergegeben (auf diese Weise await task;erhält " " sein Verhalten). Sie können also “task.GetAwaiter().GetResult()”, 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 "Aufgabe auf Fehler prüfen"

Im Allgemeinen versuche ich mein Bestes, um zu vermeiden, dass eine asynchrone Aufgabe synchron blockiert wird. Es gibt jedoch eine Handvoll Situationen, in denen ich gegen diese Richtlinie verstoße. Unter diesen seltenen Bedingungen ist meine bevorzugte MethodeGetAwaiter().GetResult() dass die Aufgabenausnahmen beibehalten werden, anstatt sie in eine zu verpacken AggregateException.

http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html

Nitin Agarwal
quelle
3
Also ist im Grunde Task.GetAwaiter().GetResult()gleichbedeutend mit await task. Ich gehe davon aus, dass die erste Option verwendet wird, wenn die Methode nicht markiert werden kann async(z. B. Konstruktor). Ist das korrekt? Wenn ja, dann kollidiert es mit der Top-Antwort @ It'sNotALie
OlegI
5
@OlegI: Task.GetAwaiter().GetResult()ist äquivalenter zu Task.Waitund Task.Result(da alle drei synchron blockieren und das Potenzial für Deadlocks haben), Task.GetAwaiter().GetResult()hat jedoch das Ausnahmeverbreitungsverhalten der Warte-Task.
Nitin Agarwal
Können Sie Deadlocks in diesem Szenario nicht vermeiden mit (Task) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Daniel Lorenz
3
@DanielLorenz: Siehe folgendes Zitat: "Die Verwendung von ConfigureAwait (false) zur Vermeidung von Deadlocks ist eine gefährliche Praxis. Sie müssten ConfigureAwait (false) für jedes Warten beim transitiven Schließen aller vom Blockierungscode aufgerufenen Methoden verwenden, einschließlich aller dritten - und Code von Drittanbietern. Die Verwendung von ConfigureAwait (false) zur Vermeidung von Deadlocks ist bestenfalls nur ein Hack. Die bessere Lösung ist "Blockieren Sie keinen asynchronen Code". " - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Nitin Agarwal
4
Ich verstehe es nicht Task.Wait und Task.Result sind vom Design her fehlerhaft? Warum sind sie nicht obsolet?
Osexpert
69

https://github.com/aspnet/Security/issues/59

"Eine letzte Bemerkung: Sie sollten es vermeiden, so viel wie möglich zu verwenden Task.Resultund Task.Waitdie innere Ausnahme immer in eine zu kapseln AggregateExceptionund die Nachricht durch eine generische zu ersetzen (ein oder mehrere Fehler sind aufgetreten), was das Debuggen erschwert. Auch wenn die synchrone Version dies nicht tun sollte." Nicht so oft verwendet werden, sollten Sie Task.GetAwaiter().GetResult()stattdessen in Betracht ziehen . "

Scyuo
quelle
20
Die Quelle, auf die hier verwiesen wird, ist jemand, der eine andere Person ohne Referenz zitiert. Betrachten Sie den Kontext: Ich kann viele Leute sehen, die GetAwaiter () blind verwenden. GetResult () überall, nachdem ich dies gelesen habe.
Jack Ukleja
2
Also sollten wir es nicht benutzen?
Tofutim
11
Wenn zwei Aufgaben mit einer Ausnahme enden, verlieren Sie die zweite in diesem Szenario Task.WhenAll(task1, task2).GetAwaiter().GetResult();.
Monsignore
Hier ist ein weiteres Beispiel: github.com/aspnet/AspNetCore/issues/13611
George Chakhidze
33

Ein weiterer Unterschied besteht darin, dass die asyncFunktion nur zurückgegeben wird, Taskanstatt dass Task<T>Sie sie nicht verwenden können

GetFooAsync(...).Result;

Wohingegen

GetFooAsync(...).GetAwaiter().GetResult();

funktioniert noch.

Ich weiß, dass der Beispielcode in der Frage für den Fall ist Task<T>, aber die Frage wird allgemein gestellt.

Nuri Tasdemir
quelle
1
Das ist nicht wahr. Schauen Sie sich meine Geige an, die genau dieses Konstrukt verwendet: dotnetfiddle.net/B4ewH8
wojciech_rak
3
@wojciech_rak In Ihrem Code verwenden Sie, Resultmit GetIntAsync()dem Task<int>nicht nur zurückgegeben wird Task. Ich schlage vor, dass Sie meine Antwort noch einmal lesen.
Nuri Tasdemir
1
Sie haben Recht, zuerst habe ich verstanden, dass Sie antworten, dass Sie nicht GetFooAsync(...).Result in eine Funktion zurückkehren können, die zurückkehrt Task. Dies ist jetzt sinnvoll, da es in C # keine void-Eigenschaften gibt ( Task.Resultist eine Eigenschaft), aber Sie können natürlich eine void-Methode aufrufen.
wojciech_rak
22

Wie bereits erwähnt, wenn Sie verwenden können await. Wenn Sie den Code synchron ausführen müssen, wie Sie es erwähnt haben .GetAwaiter().GetResult(), .Resultoder wenn .Wait()ein Risiko für Deadlocks besteht, wie viele in Kommentaren / Antworten angegeben haben. Da die meisten von uns Oneliner mögen, können Sie diese für verwenden.Net 4.5<

Erfassen eines Werts über eine asynchrone Methode:

var result = Task.Run(() => asyncGetValue()).Result;

Synchrones Aufrufen einer asynchronen Methode

Task.Run(() => asyncMethod()).Wait();

Durch die Verwendung von treten keine Deadlock-Probleme auf Task.Run.

Quelle:

https://stackoverflow.com/a/32429753/3850405

Ogglas
quelle
1

Wenn eine Aufgabe fehlerhaft ist, wird die Ausnahme erneut ausgelöst, wenn der Fortsetzungscode awaiter.GetResult () aufruft. Anstatt GetResult aufzurufen, können wir einfach auf die Result-Eigenschaft der Aufgabe zugreifen. Der Aufruf von GetResult hat den Vorteil, dass bei Fehlern der Aufgabe die Ausnahme direkt ausgelöst wird, ohne in AggregateException eingeschlossen zu werden, was einfachere und sauberere Fangblöcke ermöglicht.

Für nicht generische Aufgaben hat GetResult () einen ungültigen Rückgabewert. Seine nützliche Funktion besteht dann ausschließlich darin, Ausnahmen erneut auszulösen.

Quelle: c # 7.0 auf den Punkt gebracht

Ali Abdollahi
quelle