Gibt es ein Szenario, in dem eine solche Schreibmethode angewendet wird:
public async Task<SomeResult> DoSomethingAsync()
{
// Some synchronous code might or might not be here... //
return await DoAnotherThingAsync();
}
an Stelle von:
public Task<SomeResult> DoSomethingAsync()
{
// Some synchronous code might or might not be here... //
return DoAnotherThingAsync();
}
würde Sinn machen?
Warum return await
Konstrukt verwenden, wenn Sie direkt Task<T>
vom inneren DoAnotherThingAsync()
Aufruf zurückkehren können?
Ich sehe Code mit return await
an so vielen Stellen, dass ich glaube, ich hätte etwas verpasst. Soweit ich weiß, wäre es jedoch funktional gleichwertig, in diesem Fall keine asynchronen / wartenden Schlüsselwörter zu verwenden und die Aufgabe direkt zurückzugeben. Warum zusätzlichen Aufwand für zusätzliche await
Ebene hinzufügen ?
c#
.net
.net-4.5
async-await
TX_
quelle
quelle
Antworten:
Es gibt einen Fall , wenn sneaky
return
in normalen Verfahren undreturn await
inasync
Verfahren , verhalten sich anders: in Kombination mitusing
(oder, allgemeiner, jederreturn await
in einemtry
Block).Betrachten Sie diese beiden Versionen einer Methode:
Bei der ersten Methode wird
Dispose()
dasFoo
Objekt ausgeführt, sobald dieDoAnotherThingAsync()
Methode zurückgegeben wird. Dies ist wahrscheinlich lange bevor sie tatsächlich abgeschlossen ist. Dies bedeutet, dass die erste Version wahrscheinlich fehlerhaft ist (weilFoo
sie zu früh entsorgt wird), während die zweite Version einwandfrei funktioniert.quelle
foo.DoAnotherThingAsync().ContinueWith(_ => foo.Dispose());
Dispose()
kehrt zurückvoid
. Du würdest so etwas brauchenreturn foo.DoAnotherThingAsync().ContinueWith(t -> { foo.Dispose(); return t.Result; });
. Aber ich weiß nicht, warum Sie das tun sollten, wenn Sie die zweite Option verwenden können.{ var task = DoAnotherThingAsync(); task.ContinueWith(_ => foo.Dispose()); return task; }
. Der Anwendungsfall ist ziemlich einfach: Wenn Sie (wie die meisten) mit .NET 4.0 arbeiten, können Sie auf diese Weise immer noch asynchronen Code schreiben, der von 4.5-Apps aus gut aufgerufen wird.Foo
erst nach Abschluss der Rückgabe entsorgtTask
, was mir nicht gefällt, da dadurch unnötigerweise Parallelität entsteht.Wenn Sie nicht benötigen
async
(dh Sie können dieTask
direkt zurückgeben), dann verwenden Sie nichtasync
.Es gibt Situationen, in denen dies
return await
nützlich ist, z. B. wenn Sie zwei asynchrone Vorgänge ausführen müssen:Weitere Informationen zur
async
Leistung finden Sie im MSDN-Artikel und im Video von Stephen Toub zum Thema.Update: Ich habe einen Blog-Beitrag geschrieben , der viel detaillierter ist.
quelle
await
im zweiten Fall nützlich ist? Warum nichtreturn SecondAwait(intermediate);
?return SecondAwait(intermediate);
das Ziel auch in diesem Fall nicht erreichen? Ich denke,return await
ist auch hier überflüssig ...await
in der ersten Zeile verwenden möchten , müssen Sie es auch in der zweiten Zeile verwenden.async
Version verbraucht weniger Ressourcen (Threads) als Ihre synchrone Version.SecondAwait
ist "string", lautet die Fehlermeldung: "CS4016: Da dies eine asynchrone Methode ist, muss der Rückgabeausdruck vom Typ" string "und nicht vom Typ" Task <string> "sein.Der einzige Grund, warum Sie dies tun möchten, ist, wenn
await
der frühere Code einen anderen enthält oder wenn Sie das Ergebnis auf irgendeine Weise manipulieren, bevor Sie es zurückgeben. Eine andere Möglichkeit besteht darin, dietry/catch
Behandlung von Ausnahmen zu ändern. Wenn Sie nichts davon tun, haben Sie Recht, es gibt keinen Grund, den Aufwand für die Erstellung der Methode zu erhöhenasync
.quelle
return await
dies notwendig wäre (anstatt nur die Aufgabe des Kinderaufrufs zurückzugeben), selbst wenn im früheren Code noch etwas anderes erwartet wird . Könnten Sie bitte eine Erklärung geben?async
wie würden Sie dann auf die erste Aufgabe warten? Sie müssen die Methode markieren , wieasync
wenn Sie verwenden möchten , keine erwartet. Wenn die Methode als markiert istasync
und Sie einenawait
früheren Code haben, müssen Sieawait
die zweite asynchrone Operation ausführen, damit sie vom richtigen Typ ist. Wenn Sie es gerade entfernt haben,await
wird es nicht kompiliert, da der Rückgabewert nicht vom richtigen Typ ist. Da die Methodeasync
das Ergebnis ist, wird es immer in eine Aufgabe eingeschlossen.async
Methode keine Aufgabe zurückgeben, geben Sie das Ergebnis der Aufgabe zurück, das dann umbrochen wird.Task<Type>
explizit zurückkehren, während wirasync
diktieren, zurückzukehrenType
(was der Compiler selbst werden würdeTask<Type>
).async
ist nur syntaktischer Zucker für die explizite Verkabelung von Fortsetzungen. Sie müssen nicht brauchenasync
, etwas zu tun, aber wenn fast jeden nicht-trivialen asynchronen Betrieb zu tun , es ist dramatisch mit leichter Arbeit. Zum Beispiel verbreitet der von Ihnen bereitgestellte Code Fehler nicht so, wie Sie es möchten, und dies in noch komplexeren Situationen richtig zu machen, wird viel schwieriger. Während Sie nie brauchenasync
, sind die Situationen, die ich beschreibe, in denen es einen Mehrwert bringt, es zu nutzen.Ein weiterer Fall, den Sie möglicherweise benötigen, um auf das Ergebnis zu warten, ist der folgende:
In diesem Fall
GetIFooAsync()
muss das Ergebnis abwarten,GetFooAsync
da der Typ vonT
zwischen den beiden Methoden unterschiedlich ist undTask<Foo>
nicht direkt zugeordnet werden kannTask<IFoo>
. Aber wenn Sie das Ergebnis warten, wird es nur ,Foo
das ist direkt zuordenbarIFoo
. Dann packt die asynchrone Methode das Ergebnis einfach neuTask<IFoo>
und los.quelle
Task<>
unveränderlich ist.Wenn Sie die ansonsten einfache "Thunk" -Methode asynchronisieren, wird eine asynchrone Zustandsmaschine im Speicher erstellt, während die nicht asynchrone dies nicht tut. Während dies oft darauf hindeutet, dass die nicht asynchrone Version verwendet wird, weil sie effizienter ist (was wahr ist), bedeutet dies auch, dass Sie im Falle eines Hangs keine Beweise dafür haben, dass diese Methode am "Rückgabe- / Fortsetzungsstapel" beteiligt ist. was es manchmal schwieriger macht, den Hang zu verstehen.
Also ja, wenn perf nicht kritisch ist (und normalerweise auch nicht), werde ich all diese Thunk-Methoden asynchronisieren, damit ich die asynchrone Zustandsmaschine habe, die mir hilft, Hänge später zu diagnostizieren und um sicherzustellen, dass dies der Fall ist Wenn sich Thunk-Methoden im Laufe der Zeit weiterentwickeln, werden sie sicher fehlerhafte Aufgaben zurückgeben, anstatt sie zu werfen.
quelle
Wenn Sie return wait nicht verwenden, können Sie Ihren Stack-Trace beim Debuggen oder beim Drucken in den Protokollen bei Ausnahmen ruinieren.
Wenn Sie die Aufgabe zurückgeben, hat die Methode ihren Zweck erfüllt und befindet sich außerhalb des Aufrufstapels. Wenn Sie verwenden
return await
, belassen Sie es im Aufrufstapel.Beispielsweise:
Aufrufstapel bei Verwendung von Warten: A wartet auf die Aufgabe von B => B wartet auf die Aufgabe von C.
Aufrufliste , wenn nicht unter Verwendung Await: A Warten auf die Aufgabe von C, die B zurückgekehrt ist.
quelle
Das verwirrt mich auch und ich habe das Gefühl, dass die vorherigen Antworten Ihre eigentliche Frage übersehen haben:
Nun, manchmal möchten Sie tatsächlich eine
Task<SomeType>
, aber meistens möchten Sie tatsächlich eine InstanzSomeType
, dh das Ergebnis der Aufgabe.Aus Ihrem Code:
Eine Person, die mit der Syntax nicht vertraut ist (ich zum Beispiel), könnte denken, dass diese Methode a zurückgeben sollte
Task<SomeResult>
, aber da sie mit markiert istasync
, bedeutet dies, dass ihr tatsächlicher Rückgabetyp istSomeResult
. Wenn Sie nur verwendenreturn foo.DoAnotherThingAsync()
, geben Sie eine Aufgabe zurück, die nicht kompiliert werden kann. Der richtige Weg ist, das Ergebnis der Aufgabe zurückzugeben, also diereturn await
.quelle
var task = DoSomethingAsync();
würde Ihnen eine Aufgabe geben, nichtT
async/await
Sache gut verstanden habe. Nach meinem VerständnisTask task = DoSomethingAsync()
, währendSomething something = await DoSomethingAsync()
beide arbeiten. Die erste gibt Ihnen die richtige Aufgabe, während die zweite aufgrund desawait
Schlüsselworts das Ergebnis der Aufgabe nach Abschluss liefert . Ich könnte zum Beispiel habenTask task = DoSomethingAsync(); Something something = await task;
.