Ich habe gerade angefangen, mit async / await in .Net 4.5 herumzuspielen. Ich bin zunächst gespannt, warum das asynchrone Schlüsselwort erforderlich ist. Die Erklärung, die ich gelesen habe, war, dass es ein Marker ist, damit der Compiler weiß, dass eine Methode auf etwas wartet. Es scheint jedoch, dass der Compiler in der Lage sein sollte, dies ohne ein Schlüsselwort herauszufinden. Was macht es sonst noch?
19
Antworten:
Hier gibt es mehrere Antworten, und alle sprechen darüber, was asynchrone Methoden tun, aber keine von ihnen beantwortet die Frage, weshalb
async
sie als Schlüsselwort für die Funktionsdeklaration benötigt werden.Es geht nicht darum, "den Compiler anzuweisen, die Funktion auf besondere Weise zu transformieren".
await
allein könnte das tun. Warum? Da C # hat bereits einen anderen Mechanismus , bei dem die Anwesenheit eines speziellen Schlüsselwort in dem Verfahren Körper des Compilers verursacht extreme auszuführen (und sehr ähnlichasync/await
) Transformationen auf dem Verfahren Körper:yield
.Nur dass dies
yield
kein eigenes Schlüsselwort in C # ist und das Verständnis, warum dies so ist,async
auch erklärt. Anders als in den meisten Sprachen, die diesen Mechanismus unterstützen, können Sie in C # nicht sagen, dassyield value;
Sieyield return value;
stattdessen sagen müssen . Warum? Weil es der Sprache hinzugefügt wurde, nachdem C # bereits vorhanden war, und es durchaus vernünftig war anzunehmen, dass jemand irgendwoyield
den Namen einer Variablen verwendet haben könnte. Da es jedoch kein bereits vorhandenes Szenario gab, das<variable name> return
syntaktisch korrekt war, wurdeyield return
die Sprache erweitert, um die Einführung von Generatoren zu ermöglichen und gleichzeitig die 100% ige Abwärtskompatibilität mit vorhandenem Code zu gewährleisten.Aus diesem Grund
async
wurde es als Funktionsmodifikator hinzugefügt, um zu vermeiden, dass vorhandener Code, derawait
als Variablenname verwendet wird, beschädigt wird. Da bereits keineasync
Methoden vorhanden waren, wird kein alter Code ungültig gemacht. In neuem Code kann der Compiler anhand des vorhandenenasync
Tags feststellen , dassawait
dieses als Schlüsselwort und nicht als Bezeichner behandelt werden soll.quelle
async
Schlüsselworts entfallen kann? Natürlich wäre es eine BC-Unterbrechung, aber es könnte davon ausgegangen werden, dass dies nur sehr wenige Projekte betrifft und eine direkte Voraussetzung für ein Upgrade ist (dh das Ändern eines Variablennamens).Es ändert die Methode von einer normalen Methode in ein Objekt mit Rückruf, was einen völlig anderen Ansatz für die Codegenerierung erfordert
und wenn so etwas drastisches passiert, ist es üblich, es klar zu kennzeichnen (wir haben diese Lektion aus C ++ gelernt)
quelle
async
s gleichzeitig planen möchten, damit sie nicht nacheinander, sondern gleichzeitig ausgeführt werden (wenn mindestens Threads verfügbar sind)Task<T>
weist tatsächlich die Merkmale einerasync
Methode auf. Sie könnten beispielsweise nur eine Geschäfts- / Fabriklogik durchlaufen und dann an eine andere Methode delegieren, die zurückgibtTask<T>
.async
Methoden haben einen Rückgabetyp vonTask<T>
in der Signatur, geben aber nicht tatsächlich a zurückTask<T>
, sondern nur aT
. Um all dies ohne dasasync
Schlüsselwort herauszufinden , müsste der C # -Compiler alle möglichen Tiefenprüfungen der Methode durchführen, was sie wahrscheinlich erheblich verlangsamen und zu allen möglichen Unklarheiten führen würde.Die ganze Idee mit Schlüsselwörtern wie "async" oder "unsicher" besteht darin, Unklarheiten darüber zu beseitigen, wie der Code, den sie ändern, behandelt werden soll. Im Fall des Schlüsselworts async wird der Compiler angewiesen, die geänderte Methode als etwas zu behandeln, das nicht sofort zurückgegeben werden muss. Dies ermöglicht es dem Thread, in dem diese Methode verwendet wird, fortzufahren, ohne auf die Ergebnisse dieser Methode warten zu müssen. Es ist effektiv eine Code-Optimierung.
quelle
OK, hier ist meine Einstellung dazu.
Es gibt so etwas wie Koroutinen , die seit Jahrzehnten bekannt sind. ("Knuth and Hopper" -Klasse "seit Jahrzehnten") Sie sind Verallgemeinerungen von Unterprogrammen , indem sie nicht nur die Kontrolle über den Funktionsstart und die Rückgabeanweisung erhalten und freigeben, sondern dies auch an bestimmten Punkten ( Suspendierungspunkten ). Eine Subroutine ist eine Coroutine ohne Aufhängepunkte.
Sie sind EINFACH mit C-Makros zu implementieren, wie im folgenden Artikel über "Protothreads" gezeigt. ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Lesen Sie es. Ich werde warten...
Unterm Strich dafür ist , dass die Makros ein großen erstellen
switch
, und eincase
Etikett an jedem Aufhängungspunkt. An jedem Unterbrechungspunkt speichert die Funktion den Wert des unmittelbar folgendencase
Etiketts, damit sie weiß, wo die Ausführung beim nächsten Aufruf fortgesetzt werden soll. Und es gibt die Kontrolle an den Anrufer zurück.Dies geschieht ohne Änderung des scheinbaren Kontrollflusses des im "Protothread" beschriebenen Codes.
Stellen Sie sich vor, Sie haben eine große Schleife, die alle diese "Protothreads" der Reihe nach aufruft und gleichzeitig "Protothreads" für einen einzelnen Thread ausführt.
Dieser Ansatz hat zwei Nachteile:
Es gibt Problemumgehungen für beide:
Und wenn Sie Compiler-Unterstützung hätten, um die Umschreibungsarbeit zu erledigen, die die Makros und die Problemumgehung leisten, könnten Sie einfach Ihren Protothread-Code so schreiben, wie Sie beabsichtigen, und Suspendierungspunkte mit einem Schlüsselwort einfügen.
Und genau darum geht es
async
undawait
darum, (stapellose) Koroutinen zu erstellen.Die Coroutinen in C # werden als Objekte der Klasse (generisch oder nicht generisch) geändert
Task
.Ich finde diese Keywords sehr irreführend. Meine gedankliche Lektüre ist:
async
als "suspensible"await
als "Aussetzen bis zum Abschluss von"Task
als "Zukunft ..."Jetzt. Müssen wir die Funktion wirklich markieren
async
? Abgesehen davon, dass es die Mechanismen zum Umschreiben von Code auslösen sollte, um die Funktion zu einer Koroutine zu machen, werden einige Unklarheiten gelöst. Betrachten Sie diesen Code.Vorausgesetzt, das
async
ist nicht obligatorisch, ist dies eine Koroutine oder eine normale Funktion? Sollte der Compiler es als Coroutine umschreiben oder nicht? Beides könnte mit unterschiedlicher Semantik möglich sein.quelle