Verwirrung in Bezug auf threads und wenn asynchrone Methoden sind wirklich asynchron in C#
Las ich auf async/await und wenn Task.Yield
nützlich sein könnten und stieß dabei auf in diesem post. ich hatte eine Frage bezüglich der unten von diesem post:
Wenn Sie async/await, es gibt keine Garantie, dass die Methode, die Sie
rufen Sie, wenn Sie erwartenFooAsync()
wird asynchron ausgeführt wird.
Die interne Implementierung ist frei, um zurückzukehren, mit einem komplett
synchron-Pfad.
Dies ist ein wenig unklar ist mir wahrscheinlich, weil die definition von asynchron-in meinem Kopf ist nicht Schlange.
In meinem Kopf, da ich vor allem UI-dev, async-code ist code, der nicht auf dem Benutzeroberflächenthread ausgeführt, sondern auf einem anderen thread. Ich denke, in dem text, den ich zitierte, ist eine Methode, die nicht wirklich async wenn es blockiert auf jeden thread (auch wenn es eine thread-pool-thread zum Beispiel).
Frage:
Wenn ich eine lange laufende Aufgabe, die CPU-gebunden (sagen wir, es ist dabei eine Menge harte Mathematik), dann ausgeführt, dass die task asynchron ausgeführt werden müssen, blockieren einige thread richtig? So etwas hat eigentlich die Mathematik zu tun. Wenn ich erwarte es dann einige thread ist blockiert.
Was ist ein Beispiel für eine wirklich asynchrone Methode und wie würden Sie eigentlich arbeiten? Sind diese beschränkt auf I/O-Operationen, die die Vorteile von einigen hardware-Fähigkeiten, so dass kein thread jemals gesperrt?
- Die obligatorische Lektüre: blog.stephencleary.com/2013/11/there-is-no-thread.html
- Ich mag deine Frage, aber der Titel "Asynchrone Methoden .Net/C#" ist zu allgemein, IMO.
- Ich aktualisiert, OP ' s Frage, um genauer zu sein
- Wenn es der CPU bestimmt dann die CPU läuft in einem thread, ja, der gesamte code läuft in einem thread. Wenn es das warten auf ein Paket aus dem Netzwerk, das ist nicht code und braucht daher nicht für die Ausführung auf einem thread. (Gibt es den code irgendwo zu starten, warten und warten Sie nicht mehr, aber die tatsächliche Wartezeit ist nicht code)
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gut auf Sie für Rückfragen.
Dass der glaube ist verbreitet, aber falsch. Es gibt keine Anforderung, dass die asynchrone Ausführung von code auf jedem zweiten thread.
Vorstellen, dass Sie Kochen Frühstück. Stellen Sie einen toast in den toaster, und während Sie darauf warten, dass das toast bis pop, die Sie gehen Sie durch Ihre mail von gestern, bezahlen einige Rechnungen, und hey, der toast aufgetaucht. Sie beenden die Zahlung dieser Rechnung, und dann gehen Sie Ihre butter toast.
Wo drin hast du evtl einen zweiten Arbeiter zu beobachten, Ihre toaster?
Nicht. Threads sind Arbeitnehmer. Asynchrone workflows kann passieren, alle in einem thread. Der Punkt der asynchronen workflow vermeiden die Einstellung von mehr Arbeiter, wenn Sie können, möglichst zu vermeiden.
Hier, ich gebe Sie ein schwieriges problem zu lösen. Hier ist eine Spalte von 100 zahlen; bitte fügen Sie Sie von hand. So fügen Sie der ersten, der zweiten und der achten insgesamt. Dann fügen Sie das laufende Summe in der Dritten und bekommen insgesamt. Dann, oh, die Hölle, die zweite Seite der zahlen fehlt. Denken Sie daran, wo Sie waren, und machen uns einige toast. Oh, während der toast Toasten war ein Brief angekommen mit den restlichen zahlen. Wenn Sie fertig sind, die Butter toast, gehen, halten Sie auf addieren diese zahlen, und denken Sie daran, zu Essen der toast das nächste mal haben Sie einen freien moment.
Wo ist der Teil, wo Sie angeheuert, einen anderen Arbeitnehmer zu addieren Sie die zahlen? Rechenintensive arbeiten müssen nicht synchron sein, und muss nicht blockieren einen thread. Die Sache, die macht rechnerische Arbeit potenziell asynchrone ist die Möglichkeit, ihn zu stoppen, daran erinnern, wo Sie waren, gehen Sie etwas anderes tun, denken Sie daran, was zu tun ist nach, dass, und fortsetzen, wo Sie aufgehört haben.
Nun ist es sicherlich möglich mieten Sie ein zweiter Arbeiter, der nichts anderes macht, als zahlen zu addieren, und dann wird gefeuert. Und Sie könnten Fragen, die Arbeiter "bist du fertig?" und wenn die Antwort Nein ist, können Sie gehen ein sandwich, bis Sie fertig sind. So Sie und die Arbeiter beschäftigt sind. Aber es ist nicht eine Anforderung, dass ungleichzeitigkeiten betreffen mehrere Arbeitnehmer.
NEIN, NEIN, NEIN. Dies ist der wichtigste Teil von Ihr Missverständnis.
await
bedeutet nicht "gehen, starten Sie diesen job asynchron".await
bedeutet "ich habe ein asynchron erzeugte Ergebnis, dass hier möglicherweise nicht zur Verfügung. Wenn es nicht verfügbar ist, finden einige andere arbeiten zu tun, auf diesen thread so, dass wir nicht blockieren den thread. Erwarten ist die gegenüber, was Sie gerade gesagt haben.Asynchrone Arbeit oft mit benutzerdefinierten hardware-oder mehrere-threads, aber es muss nicht.
Glaube nicht, dass über Arbeitnehmer. Denke, über workflows. Die Essenz der ungleichzeitigkeiten ist Aufbrechen workflows in kleine Teile, so dass Sie können die Reihenfolge bestimmen, in der diese Teile müssen passieren, und dann Ausführung jeder Teil wiederum, aber so dass Teile, die keine Abhängigkeiten mit einander verzahnt werden.
In einem asynchronen workflow können Sie leicht erkennen stellen im workflow, wobei eine Abhängigkeit zwischen teilen, ausgedrückt wird. Solche Teile sind gekennzeichnet mit
await
. Das ist die Bedeutung vonawait
: der code, der folgt, hängt dieser Teil der workflow abgeschlossen wird, so dass, wenn es nicht abgeschlossen ist, gehen Sie finden eine andere Aufgabe zu tun, und später darauf zurückkommen, wenn die Aufgabe abgeschlossen ist. Der springende Punkt ist, halten die Arbeiter arbeiten, auch in einer Welt, wo erforderlich, die Ergebnisse produziert werden in Zukunft.Task
ist eine Abstraktion.Kann ich empfehlen, meine
async
intro?Fast nie. Ich finde es gelegentlich nützlich, wenn man unit-Tests.
Asynchronen code threadless.
Ich würde sagen, das ist richtig. Ich benutze den Begriff "wirklich " async" für Operationen, die nicht blockieren alle threads (und das sind nicht synchron). Ich verwenden auch den Begriff "fake " async" für Vorgänge, die erscheinen asynchron, aber nur so funktioniert, weil Sie laufen oder sperren eines thread-pool-thread.
Ja; in diesem Fall würden Sie wollen, um zu definieren, dass die Arbeit mit einer synchronen API (da es synchron arbeiten), und dann können Sie es aufrufen der UI-thread mit
Task.Run
, z.B.:Nein, der thread-pool-thread würde verwendet werden, um den code auszuführen (nicht gesperrt), und der UI-thread asynchron warten, dass der code vollständig (auch nicht blockiert).
NetworkStream.WriteAsync
(indirekt) fragt die Netzwerk-Karte zu schreiben, die einige bytes. Es gibt keine thread-verantwortlich für das schreiben der bytes ein zu einer Zeit und wartet auf jedes byte geschrieben werden. Die Netzwerk-Karte kümmert sich um all das. Wenn der Netzwerk-Karte erfolgt das schreiben der bytes, die es (irgendwann) erledigt die Aufgabe zurückgegeben, die vonWriteAsync
.Nicht ganz, obwohl I/O-Operationen sind einfache Beispiele. Ein weiteres, relativ einfaches Beispiel ist der Timer (z.B.
Task.Delay
). Sie können jedoch den Aufbau einer wirklich asynchrone API, um jede Art von "event".Task.Run
zum auslagern von UI-thread undIProgress<T>
für die Aktualisierung der Benutzeroberfläche. In Situationen, In denen die UI-updates dauern zu lange für die Reaktionsfähigkeit, dann UI-Virtualisierung ist in der Regel die bessere Lösung als ein "nachgeben, um die message queue" Art von Ansatz. Das sagte, wenn Sie wollen, um "nachgeben, um die message queue", vorbei an einem kleinen, von null verschiedenen Wert zuTask.Delay
anstattTask.Yield
, z.B.await Task.Delay(50);
.Dies bedeutet einfach, es gibt zwei Fälle beim Aufruf einer asynchronen Methode.
Die erste ist, dass bei der Rückgabe der Aufgabe an Sie, der Vorgang ist bereits abgeschlossen-dies wäre ein synchroner Weg. Das zweite ist, dass der Vorgang noch im Gange ist-das ist der async-Pfad.
Betrachten Sie diesen code, die zeigen sollte beide Wege. Wenn der Schlüssel in einem cache ist, wird er wieder synchron. Ansonsten, ein async-op gestartet und ruft zu einer Datenbank:
So verstehen, dass, können Sie ableiten, dass in der Theorie der call of
database.GetValueAsync()
können einen ähnlichen code, und selbst in der Lage sein, um zurückzukehren synchron: so auch Ihre async-Pfad bis Ende Mai laufen 100% synchron. Aber Ihr code braucht nicht zu sorgen: async/await-Griffe beiden Fällen nahtlos.Sperrung ist ein gut definierter Begriff-es bedeutet, dein thread brachte seine Ausführung Fenster, während er wartet, dass etwas (I/O, mutex, und so weiter). Also dein thread tun, die Mathematik nicht als "blockiert": es ist tatsächlich der Durchführung der Arbeit.
Einem "wirklich async-Methode" wäre eine, die einfach nie blockiert. Es endet für gewöhnlich mit I/O, aber es kann auch bedeuten
await
ing schweres Mathe-code, wenn Sie möchten, um Ihre aktuelle thread für etwas anderes (als bei der UI-Entwicklung), oder wenn Sie versuchen, die Einführung von Parallelität:Diesem Thema ist Recht riesig und mehrere Diskussionen entstehen können. Jedoch, mit
async
undawait
in C# ist als die asynchrone Programmierung. Jedoch, wie ungleichzeitigkeiten funktioniert, ist eine ganz andere Diskussion. Bis .NET 4.5, gab es keine async-und await-Schlüsselwörter und-Entwickler hatten sich zu entwickeln, die direkt gegen die Task Parallel Library (TPL). Dort wird der Entwickler hat die volle Kontrolle, Wann und wie erstellen Sie neue tasks und den threads. Jedoch hatte dies auch einen Nachteil, da nicht wirklich ein Experte zu diesem Thema, Anwendungen könnte leiden schweren performance-Probleme und bugs, die durch race-conditions zwischen threads und so weiter.Ab .NET 4.5 async-und await-Schlüsselwörter wurden eingeführt, mit einem neuen Ansatz zur asynchronen Programmierung. Die async-und await-Schlüsselwörter verursachen keine weiteren threads erstellt werden. Async-Methoden erfordern nicht multithreading, da eine asynchrone Methode wird nicht ausgeführt, auf seinen eigenen thread. Die Methode läuft auf der aktuellen Synchronisation Kontext und nutzt die Zeit in dem thread nur, wenn die Methode aktiv ist. Sie können
Task.Run
zu bewegen, CPU-gebundene Arbeit in einem hintergrund-thread, aber ein hintergrund-thread nicht helfen, mit einem Prozess, die nur darauf warten, für die Ergebnisse verfügbar sind.Den async-basierter Ansatz zur asynchronen Programmierung vorzuziehen ist vorhandenen Ansätzen in nahezu jedem Fall. Insbesondere dieser Ansatz ist besser als BackgroundWorker für IO-gebundene Vorgänge, da der code einfacher ist und Sie nicht haben, um den Schutz vor race conditions. Lesen Sie mehr über dieses Thema HIER.
Ich halte mich nicht für eine C# - schwarzen Gürtel und einige mehr erfahrene Entwickler kann Anlass zu einigen weiteren Diskussionen, sondern als ein Prinzip, von dem ich hoffe, dass ich es geschafft deine Frage zu beantworten.
Asynchron bedeutet nicht Parallel
Asynchrone impliziert nur die Parallelität. In der Tat, sogar die Verwendung von expliziten threads nicht garantieren, dass Sie gleichzeitig ausführen (z.B. wenn Sie die Fäden Affinität für die gleichen single-core -, oder häufiger, wenn es nur einen Kern in die Maschine, um mit zu beginnen).
Daher sollten Sie nicht erwarten, dass ein asynchroner Vorgang geschieht gleichzeitig etwas anderes. Asynchron bedeutet nur, dass es passieren wird, schließlich zu einem anderen Zeitpunkt (eine(Griechisch) = ohne, syn (griech.) = zusammen, khronos (Griechisch) = Zeit. => Asynchrone = nicht passiert zur gleichen Zeit).
Hinweis: Die Idee der Asynchronität ist, dass auf den Aufruf, Sie nicht zu kümmern, wenn der code tatsächlich ausgeführt wird. Dies ermöglicht es, das system zu nutzen von Parallelität, wenn möglich, zum ausführen der operation. Es kann auch sofort ausgeführt werden. Es kann sogar passieren auf dem gleichen thread... dazu später mehr.
Wenn Sie
await
der asynchronen operation, die Sie erstellen Parallelität (com (Lat.) = zusammen, currere (Latein) = führen. => "und " Concurrent" = gemeinsam). Das ist, weil Sie Fragen für die asynchrone operation abgeschlossen ist, bevor Sie fortfahren. Wir können sagen, dass die Ausführung konvergiert. Dies ist ähnlich dem Konzept der Beitritt threads.Als asynchrone nicht Parallel
Geschehen kann dies auf drei Arten:
Ist es möglich, zu verwenden
await
auf alles, was zurückTask
. Wenn Sie erhalten dieTask
es konnte bereits abgeschlossen.Doch das allein macht nicht den Eindruck erwecken, es lief synchron. In der Tat, es deuten, es lief asynchron und beendet, bevor Sie bekam die
Task
Instanz.Beachten Sie, dass Sie können
await
auf eine bereits abgeschlossene Aufgabe:Auch, wenn ein
async
Methode hat keineawait
es dann synchron läuft.Hinweis: Wie Sie bereits wissen, eine Methode, die zurückgibt einen
Task
werden können, warten auf das Netzwerk oder auf das Dateisystem, etc... damit nicht implizieren, um eine neueThread
oder enqueue etwas auf dieThreadPool
.Unter einer Synchronisierung von Rahmen, die verarbeitet wird, durch einen einzelnen thread, das Ergebnis wird sein, führen Sie die
Task
synchron, mit einigen overhead. Dies ist der Fall, der UI-thread, ich werde sprechen mehr über das, was passiert unten.Es ist möglich, schreiben Sie eine benutzerdefinierte Aufgabenplaner immer Aufgaben synchron. Auf dem gleichen thread, das funktioniert der Aufruf.
Hinweis: vor kurzem schrieb ich eine benutzerdefinierte
SyncrhonizationContext
läuft, dass Aufgaben in einem einzigen thread. Sie finden ihn unter Erstellen einer (System.Threading.Aufgaben.)Task scheduler. Es würde dazu führen, dass dieseTaskScheduler
mit einem AufrufFromCurrentSynchronizationContext
.Den Standard -
TaskScheduler
wird enqueue-die Aufrufe zu denThreadPool
. Noch beim warten auf die Bedienung, wenn es nicht auf dieThreadPool
es wird versuchen, Sie zu entfernen aus demThreadPool
und führen Sie es inline (im selben thread, der wartet... der thread wartet sowieso, es ist also nicht besetzt).Hinweis: Eine Bemerkenswerte Ausnahme ist eine
Task
markiert mitLongRunning
.LongRunning
Task
s wird in einem separaten thread ausgeführt.Ihre Frage
Wenn Sie das tun, Berechnungen, Sie müssen geschehen, auf einige thread, das Teil ist Recht.
Doch, die Schönheit der
async
undawait
ist, dass der wartende thread muss nicht gesperrt werden (mehr dazu später). Dennoch, es ist sehr einfach zu Schießen selbst in den Fuß, indem er die erwartete Aufgabe geplant, auf dem gleichen thread ausgeführt, der darauf wartet, was bei der synchronen Ausführung (das ist eine einfache Fehler in der UI-thread).Eines der zentralen Merkmale von
async
undawait
ist, dass Sie dieSynchronizationContext
von dem Anrufer. Für die meisten threads, die Ergebnisse bei der Verwendung der Standard -TaskScheduler
(die, wie bereits erwähnt, verwendet dieThreasPool
). Aber für den UI-thread es bedeutet, die Buchung der Aufgaben in der Warteschlange und dies bedeutet, dass Sie auf dem Benutzeroberflächenthread ausgeführt. Dies hat den Vorteil, dass Sie nicht haben, um zu verwendenAufzurufen
oderBeginInvoke
Zugriff auf UI-Komponenten.Bevor ich in wie
await
eineTask
aus dem UI-thread ohne ihn zu blockieren, ich möchte zu beachten, dass es möglich ist zu implementieren eineTaskScheduler
wo, wenn Sieawait
auf eineTask
Sie nicht sperren thread oder es gehen im Leerlauf, sondern Sie lassen dein thread einen anderen HolenTask
, das warten auf die Ausführung. Wenn ich Zurückportieren Aufgaben .NET 2.0 experimentierte ich mit diesem.Scheinen Sie zu verwirren asynchrone mit nicht einen thread blockiert. Wenn das, was Sie wollen, ist ein Beispiel für asynchrone Operationen .NET, die nicht erfordern einen thread blockiert, einen Weg, es zu tun, dass können Sie leicht zu fassen ist die Verwendung Fortsetzungen statt
await
. Und für die Fortsetzungen, die Sie ausführen müssen, um den auf dem UI-thread, die Sie verwenden können,Aufgabenplaner.FromCurrentSynchronizationContext
.Nicht umsetzen, Phantasie spin warten. Und damit meine ich mit einem
Timer
,Anwendung.Im Leerlauf
oder etwas ähnliches.Wenn Sie
async
Sie sagen, den compiler neu zu schreiben den code der Methode in einer Weise, die Sie brechen kann. Das Ergebnis ist ähnlich wie Fortsetzungen, mit einem viel bequemeren syntax. Wenn der thread erreicht eineawait
dieTask
geplant werden, und der thread ist frei, auch weiterhin nach den aktuellenasync
Aufruf (von der Methode). Wenn dieTask
ist gemacht, die Fortsetzung (nach derawait
) geplant ist.Für den UI-thread dies bedeutet, dass, sobald es erreicht
await
ist es frei, auch weiterhin Nachrichten verarbeiten. Sobald die erwarteteTask
ist gemacht, die Fortsetzung (nach derawait
) geplant werden. Als Ergebnis erreichenawait
bedeutet nicht, um den thread zu blockieren.Doch blind hinzufügen
async
undawait
wird nicht fix alle Ihre Probleme.Ich Ihnen ein experiment. Erhalten Sie eine neue Windows Forms-Anwendung, Tropfen in ein
Taste
und einTextBox
, und fügen Sie den folgenden code:Es blockiert die Benutzeroberfläche. Was passiert, ist, dass, wie bereits erwähnt, ist
await
verwendet automatisch dieSynchronizationContext
von der Aufrufer-thread. In diesem Fall ist der UI-thread. DaherWorkAsync
wird auf dem Benutzeroberflächenthread ausgeführt.Dies ist, was passiert:
await WorkAsync(5000)
WorkAsync(5000)
(und Terminierung Ihrer Fortsetzung) ist geplant auf der aktuellen Synchronisation Kontext des UI-thread-Synchronisation Zusammenhang... was bedeutet, dass es sendet eine Nachricht, um ihn auszuführen,WorkAsync(5000)
und planen Ihre FortsetzungWorkAsync(5000)
mit FortsetzungWorkAsync
im UI-thread läuftThread.Sleep
. Die UI ist nun irresponsive für 5 Sekunden.Das Ergebnis ist die synchrone Ausführung, mit overhead.
Ja, sollten Sie
Aufgabe.Delay
statt. Das ist nicht der Punkt; betrachtenSleep
stand für einige Berechnung. Der Punkt ist, dass nur mitasync
undawait
überall nicht geben Ihnen eine Anwendung, die automatisch parallel. Es ist viel besser, zu Holen, was Sie wollen run auf einem hintergrund-thread (z.B. auf derThreadPool
) und was wollen Sie auf dem Benutzeroberflächenthread ausgeführt.Nun versuchen Sie den folgenden code:
Finden Sie, die Sie erwarten, nicht blockiert die Benutzeroberfläche. Dies ist, weil in diesem Fall
- Thread.Schlaf
läuft jetzt auf derThreadPool
DankAufgabe.Run
. Und Dankbutton1_Click
wirdasync
, sobald der code erreichtawait
im UI-thread frei ist, weiter zu arbeiten. Nach derTask
geschehen ist, wird der code fortgesetzt, nachdem dieawait
Dank für das umschreiben durch den compiler der Methode zu ermöglichen genau das.Dies ist, was passiert:
await Task.Run(() => Work(5000))
Task.Run(() => Work(5000))
(und Terminierung Ihrer Fortsetzung) ist geplant auf der aktuellen Synchronisation Kontext des UI-thread-Synchronisation Zusammenhang... was bedeutet, dass es sendet eine Nachricht, um ihn auszuführen,Task.Run(() => Work(5000))
und planen Ihre Fortsetzung, wenn Sie fertigTask.Run(() => Work(5000))
mit Fortführung, wird dies führen Sie auf dieThreadPool
Wenn die
ThreadPool
beendet ist, wird die Fortsetzung geplant wird der rest der click-Ereignishandler ausgeführt werden, dies erfolgt durch Buchung einer anderen Meldung für den UI-thread. Wenn der UI-thread nimmt die Nachricht weiterhin in der click-Ereignis-handler wird es aktualisiert die textbox.Stephen ' s Antwort ist schon groß, so dass ich werde nicht wiederholen, was er sagte; ich habe getan meinen Gerechten Anteil an der Wiederholung der gleichen Argumente, die viele Male auf Stack Overflow (und anderswo).
Lassen Sie mich vielmehr den Fokus auf eine wichtige abstrakte Dinge zu asynchronen code: es ist nicht eine absolute qualifier. Es hat keinen Sinn zu sagen, ein Stück code ist asynchron - es ist immer asynchron mit Bezug auf etwas anderes. Das ist ganz wichtig.
Zweck der
await
zu bauen synchrone workflows auf der Oberseite der asynchrone Operationen und einige Verbindungs-synchronous code. Ihr code erscheint perfekt synchron1 im code selbst.Die Reihenfolge der Ereignisse angegeben ist, die durch die
await
Aufrufe. B verwendet die return-Wert Ein, was bedeutet, dass Sie Eine haben muss, bevor B. Die Methode mit diesem code hat einen synchronen Arbeitsablauf, und die beiden Methoden A und B sind synchron zueinander.Dies ist sehr nützlich, weil synchrone workflows sind in der Regel leichter zu denken, und noch wichtiger ist, eine Menge von workflows einfach sind synchron. Wenn B muss das Ergebnis von A ausgeführt, es muss ausgeführt, nachdem Ein2. Wenn Sie brauchen, um einen HTTP-request, um die URL für eine andere HTTP-Anforderung, müssen Sie warten, bis die erste Anfrage abgeschlossen; es hat nichts zu tun mit dem thread/task-scheduling. Vielleicht könnten wir nennen dies "inhärenter Synchronizität", abgesehen von "zufällige Synchronizität", wo Sie Kraft, um auf Dinge, die nicht bestellt werden müssen.
Ihnen sagen:
Du beschreibst code, der ausgeführt wird asynchron mit Bezug auf die UI. Das ist sicherlich eine sehr nützliche Fall für asynchrone (nicht Menschen wie UI, die nicht mehr reagiert). Aber es ist nur ein spezieller Fall eines allgemeineren Prinzips - die Dinge geschehen, die außerhalb der Reihenfolge in Bezug aufeinander. Wieder, es ist nicht absolut - Sie wollen einige Ereignisse passieren nicht in Ordnung (sprich, wenn der Benutzer zieht das Fenster oder der Fortschrittsbalken ändert, sollte das Fenster noch neu zeichnen), während andere müssen nicht geschehen aus der Bestellung (die Prozess-Taste muss nicht gedrückt werden, bevor die Last Aktion beendet).
await
in diesem Anwendungsfall nicht , dass anders als mitApplication.DoEvents
im Prinzip - es führt viele der gleichen Probleme und Vorteile.Dies ist auch der Teil, wo die original-Zitat interessant wird. Das UI muss ein thread aktualisiert werden. Dieser thread ruft einen event handler auf, der möglicherweise mit
await
. Bedeutet es, dass die Linie, woawait
verwendet wird, wird es erlauben, die Benutzeroberfläche zu aktualisieren, sich in Antwort auf die Benutzereingabe? Nein.Zuerst müssen Sie verstehen, dass
await
verwendet in seiner Argumentation, so als wäre es eine Methode aufrufen. In meinem BeispielA
müssen bereits aufgerufen, bevor der code generiertawait
alles tun können, einschließlich "Freigabe Steuerung zurück an den UI-loop". Der Rückgabewert vonA
istTask<T>
statt nurT
, die einen "möglichen Wert in die Zukunft" - undawait
generierten code überprüft, um zu sehen, ob der Wert schon da ist (in dem Fall einfach immer auf dem gleichen thread), oder nicht (das heißt, wir können den thread freigeben zurück an den UI-loop). Aber in jedem Fall, dieTask<T>
Wert selbst muss zurückgegeben wurden, ausA
.Betrachten dieser Umsetzung:
Muss der Aufrufer
A
um einen Wert zurückzugeben (eine Aufgabe, int); da es keineawait
s in der Methode, das bedeutet, dass diereturn 42;
. Aber das kann nicht geschehen, bevor der Schlaf beendet, denn die beiden Vorgänge sind synchron mit Bezug auf den thread. Der Anrufer thread blockiert, für eine zweite, unabhängig davon, ob es verwendetawait
oder nicht - die Sperrung ist inA()
selbst, nichtawait theTaskResultOfA
.Im Gegensatz dazu betrachten Sie dieses:
Sobald die Ausführung wird an die
await
es sieht, dass der erwartete task noch nicht fertig ist und gibt die Kontrolle an den Aufrufer zurück; und dieawait
im Aufrufer somit gibt die Steuerung zurück zu seine Anrufer. Wir haben es geschafft, einige der code, der asynchron mit Bezug auf die UI. Die Synchronität zwischen dem UI-thread und Einen zufälligen, und wir es entfernt.Ist der wichtige Teil hier ist: es gibt keine Möglichkeit zu unterscheiden, zwischen den beiden Implementierungen von außen ohne Prüfung der code. Nur der return-Typ ist Teil der Signatur der Methode - es nicht sagen, die Methode wird asynchron ausgeführt werden, nur, dass es kann. Dies kann für jede Reihe von guten Gründen, so hat es keinen Sinn, es zu bekämpfen - es gibt zum Beispiel keinen Sinn, brechen den thread der Ausführung, wenn das Ergebnis bereits verfügbar ist:
Wir brauchen, um einige Arbeit zu tun. Einige Ereignisse asynchron ausführen kann, die mit Bezug auf andere (in diesem Fall
ComputeAllTheFuzz
ist unabhängig von der HTTP-request) und asynchron sind. Aber an einem gewissen Punkt, wir müssen zurück zu einer synchronen workflow (zum Beispiel, etwas, das erfordert, dass sowohl das Ergebnis derComputeAllTheFuzz
und die HTTP-Anfrage). Das ist dieawait
Punkt, der synchronisiert die Ausführung wieder (wenn Sie mehrere asynchrone workflows, die Sie verwenden würden, so etwas wieTask.WhenAll
). Allerdings, wenn die HTTP-Anforderung verwaltet abgeschlossen ist, bevor Sie die Berechnung, gibt es keinen Punkt in der Freigabe-Regelung an derawait
Punkt - wir können einfach weiterhin auf dem gleichen thread. Es gab keine Verschwendung von CPU - keine Sperrung des Threads; es ist nützlich, CPU-Arbeit. Aber wir geben nicht eine Möglichkeit das UI zu aktualisieren.Dies ist natürlich, warum dieses Muster ist in der Regel vermieden, in mehr Allgemeinen asynchronen Methoden. Es ist nützlich für einige Anwendungen der asynchronen code (Vermeidung von Verschwendung threads und CPU-Zeit), andere aber nicht (wobei die UI-responsive). Wenn Sie erwarten, dass diese Methode zu halten, die UI reaktionsschnell Weg, du wirst nicht glücklich sein mit dem Ergebnis. Aber wenn Sie verwenden es als Teil eines web-service, zum Beispiel, es funktioniert großartig - der Schwerpunkt dort liegt auf der Vermeidung von Verschwendung threads, nicht Einhaltung der UI-responsive (das ist bereits von asynchron aufrufen des service-Endpunkt - es gibt keinen Vorteil davon, das gleiche zu tun, wieder auf der service-Seite).
Kurz
await
können Sie code schreiben, der asynchron mit Bezug auf den Aufrufer aus. Es muss nicht auf eine Magische macht der Asynchronität ist, ist es nicht asynchron mit Bezug auf alles, es nicht verhindern, dass Sie mit der CPU oder blockieren von threads. Es gibt Ihnen die Werkzeuge, um einfach einen synchronen workflow-out-of-asynchrone Operationen und präsentieren ein Teil des ganzen Workflows asynchron in Bezug auf seine Anrufer.Betrachten wir ein UI-event-handler. Wenn die einzelnen asynchrone Vorgänge passieren nicht brauchen, um einen thread auszuführen (z.B. asynchrone I/O), Teil der asynchronen Methode kann anderen code ausführen auf der ursprüngliche thread (und die Benutzeroberfläche bleibt responsive in diesen teilen). Wenn der Betrieb muss die CPU/thread wieder, es kann oder kann nicht verlangen, dass die original thread, um die Arbeit fortzusetzen. Wenn es funktioniert, das UI wird wieder gesperrt für die Dauer der CPU-Arbeit; wenn es nicht (die "awaiter" gibt diese mit
ConfigureAwait(false)
), die UI-code parallel ausgeführt. Vorausgesetzt, es ist genug Ressourcen, um beide, natürlich. Wenn Sie die Benutzeroberfläche zu bleiben reaktionsschnell Weg zu allen Zeiten, können Sie nicht den UI-thread für jede Ausführung lange genug, um zu erkennen - auch wenn das bedeutet, dass Sie einwickeln einem unzuverlässigen "in der Regel asynchron, aber manchmal blockiert für ein paar Sekunden" async " - Methode in einerTask.Run
. Es gibt Kosten und Vorteile für beide Ansätze - es ist ein trade-off, wie bei allen Ingenieurwissenschaften 🙂Hier ist der asynchrone code, das zeigt, wie async /await kann code block-und release-regler zu einem anderen fließen, dann wieder Steuern, aber nicht brauchen einen thread.
So ist es, dass das loslassen von Kontrolle und resynching, wenn Sie wollen Ergebnisse, dass die Schlüssel mit async/await ( die gut funktioniert mit threading )
HINWEIS: es werden Keine Threads blockiert wurden in der Herstellung dieser code 🙂
Ich denke manchmal, dass die Verwirrung kommen könnte "Aufgaben" die bedeutet nicht, dass etwas läuft auf einem eigenen thread. Es bedeutet nur eine Sache zu tun, async /await-können die Aufgaben werden aufgeteilt in Phasen und Koordinierung dieser verschiedenen Stufen in einem Fluss.
Es ist ein bisschen wie Kochen Sie das Rezept Folgen. Sie tun müssen alle Vorarbeiten vor der Montage der Schale Kochen. So schalten Sie den Ofen, starten Sie schneiden die Dinge, Gitter, usw. Dann wartest du auf die temp im Ofen und warten auf die Vorbereitungsarbeit. Sie könnte es selbst tun, tauschen zwischen Aufgaben in einer Weise, die logisch scheint (Aufgaben /async /await), aber man kann jemand anderes helfen, Käse reiben, während Sie hacken, Karotten (threads), um Dinge zu erledigen schneller.