Ich dachte, dass sie im Grunde dasselbe sind - Programme schreiben, die Aufgaben zwischen Prozessoren aufteilen (auf Maschinen mit 2+ Prozessoren). Dann lese ich das , das sagt:
Asynchrone Methoden sollen nicht blockierende Vorgänge sein. Ein Warte-Ausdruck in einer asynchronen Methode blockiert den aktuellen Thread nicht, während die erwartete Aufgabe ausgeführt wird. Stattdessen meldet der Ausdruck den Rest der Methode als Fortsetzung und gibt die Kontrolle an den Aufrufer der asynchronen Methode zurück.
Die Schlüsselwörter async und await führen nicht dazu, dass zusätzliche Threads erstellt werden. Asynchrone Methoden erfordern kein Multithreading, da eine asynchrone Methode nicht in einem eigenen Thread ausgeführt wird. Die Methode wird im aktuellen Synchronisationskontext ausgeführt und verwendet die Zeit im Thread nur, wenn die Methode aktiv ist. Sie können Task.Run verwenden, um CPU-gebundene Arbeit in einen Hintergrundthread zu verschieben. Ein Hintergrundthread hilft jedoch nicht bei einem Prozess, der nur darauf wartet, dass Ergebnisse verfügbar werden.
und ich frage mich, ob jemand das für mich ins Englische übersetzen kann. Es scheint einen Unterschied zwischen Asynkronizität (ist das ein Wort?) Und Threading zu machen und zu implizieren, dass Sie ein Programm haben können, das asynchrone Aufgaben, aber kein Multithreading hat.
Jetzt verstehe ich die Idee von asynchronen Aufgaben wie dem Beispiel auf Seite. 467 von Jon Skeets C # In Depth, dritte Ausgabe
async void DisplayWebsiteLength ( object sender, EventArgs e )
{
label.Text = "Fetching ...";
using ( HttpClient client = new HttpClient() )
{
Task<string> task = client.GetStringAsync("http://csharpindepth.com");
string text = await task;
label.Text = text.Length.ToString();
}
}
Das async
Schlüsselwort bedeutet " Diese Funktion wird, wann immer sie aufgerufen wird, nicht in einem Kontext aufgerufen, in dem ihre Fertigstellung erforderlich ist, damit nach dem Aufruf alles aufgerufen wird."
Mit anderen Worten, schreiben Sie es mitten in einer Aufgabe
int x = 5;
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);
, da DisplayWebsiteLength()
hat nichts mit x
oder zu tun y
, wird DisplayWebsiteLength()
"im Hintergrund" ausgeführt, wie
processor 1 | processor 2
-------------------------------------------------------------------
int x = 5; | DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0); |
Das ist natürlich ein dummes Beispiel, aber bin ich richtig oder total verwirrt oder was?
(Außerdem bin ich verwirrt darüber, warum sender
und werde e
im Hauptteil der obigen Funktion nie verwendet.)
quelle
sender
unde
schlagen vor, dass dies tatsächlich ein Event-Handler ist - so ziemlich der einzige Ort, an demasync void
es wünschenswert ist. Dies wird höchstwahrscheinlich durch Klicken auf eine Schaltfläche oder ähnliches aufgerufen. Das Ergebnis ist, dass diese Aktion in Bezug auf den Rest der Anwendung vollständig asynchron ausgeführt wird. Aber es ist immer noch alles in einem Thread - dem UI-Thread (mit einem winzigen Zeitabschnitt in einem IOCP-Thread, der den Rückruf an den UI-Thread sendet).DisplayWebsiteLength
Codebeispiel: Sie sollten ihn nichtHttpClient
in einerusing
Anweisung verwenden. Unter hoher Last kann der Code die Anzahl der verfügbaren Sockets erschöpfen, was zu SocketException-Fehlern führt. Weitere Informationen zu Unsachgemäßer Instanziierung .Antworten:
Ihr Missverständnis ist äußerst häufig. Vielen Menschen wird beigebracht, dass Multithreading und Asynchronität dasselbe sind, aber nicht.
Eine Analogie hilft normalerweise. Sie kochen in einem Restaurant. Eier und Toast werden bestellt.
Ist es nun sinnvoll, dass Multithreading nur eine Art von Asynchronität ist? Beim Threading geht es um Arbeiter; Bei Asynchronität geht es um Aufgaben . In Multithread-Workflows weisen Sie Arbeitern Aufgaben zu. In asynchronen Single-Threaded-Workflows haben Sie ein Diagramm mit Aufgaben, bei denen einige Aufgaben von den Ergebnissen anderer abhängen. Wenn jede Aufgabe abgeschlossen ist, wird der Code aufgerufen, der die nächste Aufgabe plant, die ausgeführt werden kann, wenn die Ergebnisse der gerade abgeschlossenen Aufgabe vorliegen. Aber Sie brauchen (hoffentlich) nur einen Arbeiter, um alle Aufgaben auszuführen, nicht einen Arbeiter pro Aufgabe.
Es wird hilfreich sein zu erkennen, dass viele Aufgaben nicht prozessorgebunden sind. Für prozessorgebundene Aufgaben ist es sinnvoll, so viele Worker (Threads) einzustellen, wie es Prozessoren gibt, jedem Worker eine Aufgabe zuzuweisen, jedem Worker einen Prozessor zuzuweisen und jeden Prozessor die Aufgabe zu übernehmen, nur das Ergebnis als zu berechnen so schnell wie möglich. Für Aufgaben, die nicht auf einen Prozessor warten, müssen Sie jedoch überhaupt keinen Mitarbeiter zuweisen. Sie warten nur auf die Nachricht, dass das Ergebnis verfügbar ist, und tun etwas anderes, während Sie warten . Wenn diese Nachricht eintrifft, können Sie die Fortsetzung der abgeschlossenen Aufgabe als nächstes auf Ihrer Aufgabenliste zum Abhaken planen.
Schauen wir uns Jons Beispiel genauer an. Was geschieht?
text
den Rest der Methode zuweisen und ausführen können .Es ist genau wie in meiner Analogie. Jemand fragt Sie nach einem Dokument. Sie senden die E-Mail für das Dokument ab und erledigen weitere Arbeiten. Wenn es in der Post ankommt, werden Sie benachrichtigt, und wenn Sie Lust dazu haben, erledigen Sie den Rest des Workflows - öffnen Sie den Umschlag, zahlen Sie die Zustellgebühren, was auch immer. Sie müssen keinen anderen Mitarbeiter einstellen, um das alles für Sie zu erledigen.
quelle
In-Browser-Javascript ist ein großartiges Beispiel für ein asynchrones Programm ohne Threads.
Sie müssen sich nicht darum kümmern, dass mehrere Codeteile gleichzeitig dieselben Objekte berühren: Jede Funktion wird beendet, bevor ein anderes Javascript auf der Seite ausgeführt werden darf.
Wenn Sie jedoch so etwas wie eine AJAX-Anfrage ausführen, wird überhaupt kein Code ausgeführt, sodass anderes Javascript auf Dinge wie Klickereignisse reagieren kann, bis diese Anfrage zurückkommt und den damit verbundenen Rückruf aufruft. Wenn einer dieser anderen Ereignishandler noch ausgeführt wird, wenn die AJAX-Anforderung zurückkommt, wird sein Handler erst aufgerufen, wenn sie fertig sind. Es wird nur ein JavaScript- "Thread" ausgeführt, obwohl Sie das, was Sie getan haben, effektiv anhalten können, bis Sie die benötigten Informationen haben.
In C # -Anwendungen geschieht dasselbe immer dann, wenn Sie sich mit UI-Elementen befassen. Sie dürfen nur dann mit UI-Elementen interagieren, wenn Sie sich im UI-Thread befinden. Wenn der Benutzer auf eine Schaltfläche geklickt hat und Sie eine große Datei von der Festplatte lesen möchten, kann ein unerfahrener Programmierer den Fehler machen, die Datei im Click-Event-Handler selbst zu lesen, was dazu führen würde, dass die Anwendung bis zum "Einfrieren" Das Laden der Datei wurde beendet, da auf weitere Klick-, Schwebeflug- oder andere UI-bezogene Ereignisse erst reagiert werden darf, wenn dieser Thread freigegeben wurde.
Eine Option, die Programmierer verwenden können, um dieses Problem zu vermeiden, besteht darin, einen neuen Thread zum Laden der Datei zu erstellen und dann dem Code des Threads mitzuteilen, dass beim Laden der Datei der verbleibende Code erneut auf dem UI-Thread ausgeführt werden muss, damit UI-Elemente aktualisiert werden können basierend auf dem, was es in der Datei gefunden hat. Bis vor kurzem war dieser Ansatz sehr beliebt, da er durch die C # -Bibliotheken und die Sprache vereinfacht wurde, aber grundlegend komplizierter ist, als er sein muss.
Wenn Sie darüber nachdenken, was die CPU tut, wenn sie eine Datei auf der Ebene der Hardware und des Betriebssystems liest, gibt sie im Grunde eine Anweisung aus, Daten von der Festplatte in den Speicher zu lesen und das Betriebssystem mit einem "Interrupt" zu treffen "wenn der Lesevorgang abgeschlossen ist. Mit anderen Worten, das Lesen von der Festplatte (oder einer beliebigen E / A) ist von Natur aus eine asynchrone Operation. Das Konzept eines Threads, der darauf wartet, dass diese E / A abgeschlossen wird, ist eine Abstraktion, die die Bibliotheksentwickler erstellt haben, um das Programmieren zu vereinfachen. Es ist nicht nötig.
Jetzt haben die meisten E / A-Vorgänge in .NET eine entsprechende
...Async()
Methode, die Sie aufrufen können und dieTask
fast sofort eine zurückgibt . Sie können Rückrufe hinzufügenTask
, um den Code anzugeben, den Sie ausführen möchten, wenn der asynchrone Vorgang abgeschlossen ist. Sie können auch angeben, auf welchem Thread dieser Code ausgeführt werden soll, und Sie können ein Token bereitstellen, das der asynchrone Vorgang von Zeit zu Zeit überprüfen kann, um festzustellen, ob Sie die asynchrone Aufgabe abbrechen möchten, damit sie ihre Arbeit schnell beenden kann und anmutig.Bis zum
async/await
Hinzufügen der Schlüsselwörter war C # viel offensichtlicher darüber, wie Rückrufcode aufgerufen wird, da diese Rückrufe in Form von Delegaten vorliegen, die Sie der Aufgabe zugeordnet haben. Um Ihnen weiterhin den Vorteil der Verwendung der...Async()
Operation zu bieten und gleichzeitig die Komplexität des Codes zu vermeiden,async/await
wird die Erstellung dieser Delegaten abstrahiert. Aber sie sind immer noch im kompilierten Code enthalten.So kann Ihr UI-Ereignishandler
await
eine E / A-Operation ausführen, den UI-Thread für andere Aufgaben freigeben und nach dem Lesen der Datei mehr oder weniger automatisch zum UI-Thread zurückkehren - ohne dies jemals tun zu müssen Erstelle einen neuen Thread.quelle