In diesem MSDN-Artikel wird der folgende Beispielcode bereitgestellt (der Kürze halber leicht bearbeitet):
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
Die FindAsync
Methode ruft ein Department
Objekt anhand seiner ID ab und gibt a zurück Task<Department>
. Dann wird die Abteilung sofort überprüft, um festzustellen, ob sie null ist. Soweit ich weiß, wird die Abfrage des Task-Werts auf diese Weise die Codeausführung blockieren, bis der Wert der erwarteten Methode zurückgegeben wird, wodurch dies effektiv zu einem synchronen Aufruf wird.
Warum würdest du das jemals tun? Wäre es nicht einfacher, nur die synchrone Methode aufzurufen Find(id)
, wenn Sie trotzdem sofort blockieren würden?
c#
.net
asp.net-mvc
async
Robert Harvey
quelle
quelle
... else return null;
Dann müssten Sie überprüfen, ob die Methode tatsächlich die von Ihnen gewünschte Abteilung gefunden hat.Antworten:
Nicht ganz.
Beim Aufrufen wird
await db.Departments.FindAsync(id)
die Aufgabe abgesetzt und der aktuelle Thread zur Verwendung durch andere Vorgänge an den Pool zurückgegeben. Der Ablauf der Ausführung ist blockiert (department
wenn ich die Dinge richtig verstehe, ist dies unabhängig von der Verwendung direkt danach), aber der Thread selbst kann von anderen Dingen verwendet werden, während Sie warten, bis der Vorgang außerhalb des Computers abgeschlossen ist (und durch einen Ereignis- oder Abschlussport signalisiert).Wenn Sie angerufen haben,
d.Departments.Find(id)
sitzt der Thread dort und wartet auf die Antwort, obwohl der größte Teil der Verarbeitung in der Datenbank ausgeführt wird.Sie setzen CPU-Ressourcen effektiv frei, wenn die Festplatte gebunden ist.
quelle
await
getan wurde, war, den Rest der Methode als Fortsetzung im selben Thread zu signieren (es gibt Ausnahmen; einige asynchrone Methoden drehen ihren eigenen Thread hoch) oder dieasync
Methode als Fortsetzung im selben Thread zu signieren und zuzulassen Der verbleibende Code muss ausgeführt werden (wie Sie sehen können, ist mir nicht ganz klar, wie dasasync
funktioniert). Was Sie beschreiben, klingt eher wie eine raffinierte Form vonThread.Sleep(untilReturnValueAvailable)
ConfigureAwait
iirc).await
seinen Aufruf anpublic async Task<ActionResult> Details(int? id)
. Andernfalls wird der ursprüngliche Anruf nur blockiert und wartet aufdepartment == null
die Lösung.await ...
Rückkehr ist derFindAsync
Anruf beendet. Das ist, was das Warten macht. Es heißt warten, weil es Ihren Code auf Dinge warten lässt. (Beachten Sie jedoch, dass dies nicht mit dem Warten des aktuellen Threads identisch ist.)Ich hasse es wirklich, dass keines der Beispiele zeigt, wie es möglich ist, ein paar Zeilen zu warten, bevor man auf die Aufgabe wartet. Bedenken Sie.
Dies ist die Art von Code, die die Beispiele anregen, und Sie haben Recht. Das hat wenig Sinn. Es gibt den Haupt-Thread frei, um andere Dinge zu tun, wie z. B. auf Eingaben über die Benutzeroberfläche zu reagieren, aber die wahre Stärke von async / await ist, dass ich problemlos andere Dinge tun kann, während ich darauf warte, dass eine möglicherweise lange laufende Aufgabe abgeschlossen ist. Der obige Code wird "blockieren" und warten, bis die Druckzeile ausgeführt ist, bis wir Foo & Bar erhalten haben. Es besteht kein Grund zu warten. Wir können das verarbeiten, während wir warten.
Mit dem neu geschriebenen Code hören wir jetzt nicht auf und warten auf unsere Werte, bis wir müssen. Ich bin immer auf der Suche nach solchen Möglichkeiten. Wenn Sie überlegen sind, wann Sie auf uns warten, kann dies zu erheblichen Leistungsverbesserungen führen. Wir haben heutzutage mehrere Kerne, können sie aber auch verwenden.
quelle
await
und den Thread stattdessen ganz andere Dinge tun lassen.Hinter den Kulissen passiert also noch mehr. Async / Await ist syntaktischer Zucker. Schauen Sie sich zunächst die Signatur der FindAsync-Funktion an. Es gibt eine Aufgabe zurück. Schon sehen Sie die Magie des Schlüsselworts, es entpackt diese Aufgabe in eine Abteilung.
Die aufrufende Funktion blockiert nicht. Was passiert, ist, dass die Zuweisung zu Abteilung und alles, was auf das Schlüsselwort await folgt, in einen Abschluss eingeschlossen und für alle Absichten und Zwecke an die Task.ContinueWith-Methode übergeben wird (die FindAsync-Funktion wird automatisch in einem anderen Thread ausgeführt).
Natürlich passiert hinter den Kulissen noch mehr, da die Operation auf den ursprünglichen Thread zurückgeführt wird (sodass Sie sich nicht mehr um die Synchronisierung mit der Benutzeroberfläche kümmern müssen, wenn Sie eine Hintergrundoperation ausführen) und wenn die aufrufende Funktion asynchron ist ( und asynchron aufgerufen wird, passiert das Gleiche auf dem Stack.
Was also passiert, ist, dass Sie die Magie von Async-Operationen ohne die Fallen erhalten.
quelle
Nein, es kehrt nicht sofort zurück. Durch das Warten wird der Methodenaufruf asynchron. Wenn FindAsync aufgerufen wird, gibt die Details-Methode eine nicht abgeschlossene Aufgabe zurück. Wenn FindAsync beendet ist, wird das Ergebnis in die Abteilungsvariable zurückgegeben und der Rest der Details-Methode fortgesetzt.
quelle
async
await
Im Allgemeinen werden keine neuen Threads erstellt, und selbst wenn dies der Fall ist, müssen Sie immer noch auf diesen Abteilungswert warten, um herauszufinden, ob er null ist.public async Task<ActionResult>
auchawait
bearbeitet werden muss.await
Darf nicht mit.Wait()
oder gemischt werden.Result
, da dies zu Deadlocks führen kann. Die asynchrone / erwartete Kette endet normalerweise bei einer Funktion mit einerasync void
Signatur, die hauptsächlich für Ereignishandler oder für Funktionen verwendet wird, die direkt von UI-Elementen aufgerufen werden.Ich stelle mir "asynchron" gerne wie einen Vertrag vor, einen Vertrag, der besagt, dass "ich dies bei Bedarf asynchron ausführen kann, aber Sie können mich auch wie jede andere Synchronfunktion aufrufen".
Das heißt, ein Entwickler hat Funktionen erstellt, und einige Designentscheidungen haben dazu geführt, dass einige Funktionen als "asynchron" gekennzeichnet wurden. Dem Aufrufer / Verbraucher der Funktionen steht es frei, diese nach eigenem Ermessen zu verwenden. Wie Sie sagen, können Sie entweder unmittelbar vor dem Funktionsaufruf warten und darauf warten. Auf diese Weise haben Sie die Funktion wie eine synchrone Funktion behandelt. Wenn Sie jedoch möchten, können Sie sie ohne Wartezeit als aufrufen
und nach etwa 10 Zeilen nach unten die Funktion, die Sie aufrufen
daher wird es als asynchrone Funktion behandelt.
Es liegt an dir.
quelle
"wenn du sowieso sofort blocken willst" ist die Antwort "Ja". Nur wenn Sie eine schnelle Antwort benötigen, ist wait / async sinnvoll. Beispielsweise wird der UI-Thread zu einer wirklich asynchronen Methode, der UI-Thread kehrt zurück und hört weiterhin auf das Klicken auf die Schaltfläche, während der Code unter "wait" von einem anderen Thread ausgeführt wird und schließlich das Ergebnis abruft.
quelle