Ich habe das Web durchsucht und viele Fragen zu task.run vs async abgewartet, aber es gibt dieses spezielle Anwendungsszenario, in dem ich den Unterschied nicht wirklich verstehe. Szenario ist ziemlich einfach, glaube ich.
%Vor%vs
%Vor%Dabei ist LongProcess eine asynchrone Methode mit einigen asynchronen Aufrufen, wie zum Beispiel der Aufruf von db mit awarte ExecuteReaderAsync ().
Frage:
Gibt es in diesem Szenario einen Unterschied zwischen den beiden? Jede Hilfe oder Eingabe geschätzt, danke!
Task.Run
darf die Operation für die Verarbeitung in einem anderen Thread posten. Das ist der einzige Unterschied.
Dies kann nützlich sein - wenn zum Beispiel LongProcess
nicht wirklich asynchron ist, wird der Aufrufer schneller zurückkommen. Aber für eine wirklich asynchrone Methode macht es keinen Sinn, Task.Run
zu verwenden, und dies kann zu unnötigem Verschwendung führen.
Seien Sie jedoch vorsichtig, da sich das Verhalten von Task.Run
basierend auf der Überladungsauflösung ändert. In Ihrem Beispiel wird die Überladung Func<Task>
gewählt, die (richtigerweise) auf den Abschluss von LongProcess
wartet. Wenn jedoch ein Delegat ohne Aufgabe verwendet wird, wartet Task.Run
nur auf die Ausführung bis zum ersten await
(beachten Sie, dass sich TaskFactory.StartNew
immer verhält, also don nutze das nicht).
Sehr oft denken Leute, dass async-wait von mehreren Threads erledigt wird. Tatsächlich wird alles durch einen Thread erledigt.
Die Sache, die mir sehr geholfen hat, async-wait zu verstehen, ist dieses Interview mit Eric Lippert über async-erwarten . Irgendwo in der Mitte vergleicht er async mit einem Koch, der warten muss, bis etwas Wasser kocht. Anstatt nichts zu tun, schaut er sich um, ob es noch etwas anderes zu tun gibt, wie die Zwiebeln zu schneiden. Wenn das beendet ist und das Wasser immer noch nicht kocht, prüft er, ob noch etwas zu tun ist, und so weiter, bis er nichts anderes zu tun hat, als zu warten. In diesem Fall kehrt er zu der ersten Sache zurück, auf die er gewartet hat.
Wenn Ihre Prozedur eine erwartete Funktion aufruft, sind wir sicher, dass irgendwo in dieser erwarteten Funktion ein Aufruf einer erwarteten Funktion erfolgt, andernfalls wäre die Funktion nicht zu erwarten. In der Tat wird Ihr Compiler Sie warnen, wenn Sie vergessen haben, irgendwo in Ihrer erwarteten Funktion zu warten.
Wenn Ihre erwartete Funktion die andere erwartete Funktion aufruft, dann tritt der Thread in diese andere Funktion ein und beginnt mit den Dingen in dieser Funktion und geht tiefer in andere Funktionen, bis er eine Wartezeit erfüllt.
Anstatt auf die Ergebnisse zu warten, geht der Thread in seinem Aufruf-Stack nach oben, um zu sehen, ob es weitere Code-Stücke gibt, die er verarbeiten kann, bis er ein Warten sieht. Geh wieder in den Call-Stack, bearbeite bis zum Warten, usw. Sobald alle warten, sucht der Thread nach dem unteren warten und setzt fort, sobald dies abgeschlossen ist.
Das hat den Vorteil, dass, wenn der Aufrufer Ihrer erwarteten Funktion nicht das Ergebnis Ihrer Funktion benötigt, aber andere Dinge tun kann, bevor das Ergebnis benötigt wird, können diese anderen Dinge vom Thread erledigt werden, anstatt in Ihrem zu warten Funktion.
Ein Anruf, ohne sofort auf das Ergebnis zu warten, würde so aussehen:
%Vor%Beachten Sie, dass in diesem Szenario alles von einem Thread erledigt wird. Solange dein Thread etwas zu tun hat, wird er beschäftigt sein.
Addition. Es ist nicht wahr, dass nur ein Thread beteiligt ist. Jeder Thread, der nichts zu tun hat, kann nach einer Wartezeit mit der Verarbeitung des Codes fortfahren. Wenn Sie die Thread-ID überprüfen, können Sie sehen, dass diese ID nach dem Warten geändert werden kann. Der fortlaufende Thread hat das gleiche
context
wie der ursprüngliche Thread, sodass Sie so tun können, als wäre es der ursprüngliche Thread. Sie müssen nicht nachInvokeRequired
suchen, es müssen keine Mutexes oder kritische Abschnitte verwendet werden. Für Ihren Code ist das so, als ob es einen Thread gibt.
Der Link zum Artikel am Ende dieser Antwort erklärt etwas mehr über den Thread-Kontext
Sie werden erwartete Funktionen hauptsächlich dort sehen, wo ein anderer Prozess etwas tun muss, während Ihr Thread nur warten muss, bis die andere Sache fertig ist, wie Daten über das Internet senden, eine Datei speichern, mit einer Datenbank kommunizieren usw.
Allerdings müssen manchmal einige schwere Berechnungen durchgeführt werden, und Sie möchten, dass Ihr Thread frei ist, etwas anderes zu tun, etwa auf Benutzereingaben zu reagieren. In diesem Fall können Sie eine erwartete Aktion starten, als ob Sie eine asynchrone Funktion aufgerufen hätten.
%Vor%Jetzt macht ein anderer Thread die schweren Berechnungen, während Ihr Thread frei ist, andere Dinge zu tun. Sobald es beginnt zu warten, kann der Anrufer Dinge tun, bis er wartet. Effektiv kann Ihr Thread ziemlich frei auf Benutzereingaben reagieren. Dies wird jedoch nur dann der Fall sein, wenn alle darauf warten. Während Ihr Thread beschäftigt ist, Dinge zu tun, kann Ihr Thread nicht auf Benutzereingaben reagieren. Stellen Sie daher immer sicher, dass Sie, wenn Sie glauben, dass Ihr UI-Thread einige Zeit mit der Verarbeitung beschäftigt sein muss, Task.Run verwenden und einen anderen Thread tun lassen müssen
Ein weiterer Artikel, der mir geholfen hat: Async-Await von dem brillanten Erklärer Stephen Cleary
Tags und Links c# async-await asynchronous