async & await - Umfrage nach Alternativen [geschlossen]

15

Jetzt, da wir wissen, was auf c # 5 zukommt , haben wir offenbar noch eine Möglichkeit, die Auswahl der beiden neuen Schlüsselwörter für " Asynchrony " zu beeinflussen, die gestern von Anders Heijsberg auf der PDC10 angekündigt wurden .

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Eric Lippert hat eine Erklärung für die Auswahl der aktuellen zwei Schlüsselwörter und die Art und Weise, wie sie in Usability-Studien missverstanden wurden. Die Kommentare haben mehrere andere Aussagen.

Bitte - ein Vorschlag pro Antwort, Duplikate werden geknackt.

Benjol
quelle
Übrigens "sprachintegrierte asynchrone Programmierung" gibt uns LIAP, rollt nicht ganz die Zunge ab wie LINQ;)
Benjol
1
Es sei denn, es ist ein deutlicher Sprung.
Conrad Frix
3
Die Abkürzung "Sprachintegrierte asynchrone Laufzeit" passt gut zusammen.
Glenatron
Das muss off-topic sein.
DeadMG

Antworten:

6

Da mir die Bedeutung / Notwendigkeit von nicht klar ist async, kann ich nicht wirklich darüber streiten, aber mein bester Vorschlag für das Ersetzen awaitist:

yield while (schau! keine neuen Keywords)

Hinweis mit Gedanken über das ein bisschen mehr, frage ich mich , ob die Wiederverwendung von whileauf diese Weise eine gute Idee ist - die natürliche Tendenz danach eine boolean zu erwarten wäre.

(Denkt, gute Keywords zu finden ist wie gute Domainnamen zu finden :)

Benjol
quelle
+1 und übrigens hast du mich geschlagen, seinen Blogeintrag um 7 Minuten zu kommentieren ...
Notiz an
Sie geben jedoch nicht unbedingt die Ausführung auf, wenn die Aufgabe bereits abgeschlossen wurde. Aber Sie warten immer auf den Abschluss der Aufgabe (obwohl Sie nie warten).
Allon Guralnek
@Allon Sie müssen nicht unbedingt den Loop-Body von while(x) {...}beiden ausführen , wenn dies xfalsch ist.
Hinweis für sich selbst - denken Sie an einen Namen
@ Hinweis: Nun, es gibt kein Verb in while. Wenn Sie ein Verb wie hinzufügen do, erhalten Sie do {...} while (x), das den Körper unabhängig von x (mindestens einmal) ausführt. Ihr Vorschlag von yield whilescheint sehr ähnlich zu sein do while, aber mit entgegengesetzten Garantien für die Ausführung des Verbs, was ein bisschen irreführend sein könnte (aber nicht sehr wichtig). Das, was ich am wenigsten mag, yieldist, dass es die Implementierung eines Mechanismus impliziert. Der springende Punkt bei async/ awaitist, dass Sie eine asynchrone Operation in einem synchronen Stil schreiben. yieldbricht diesen synchronen Stil.
Allon Guralnek
Ist ein neues Keyword unbedingt eine schlechte Sache? Soweit ich weiß, wird das awaitSchlüsselwort vom Kontext erkannt, sodass Sie nach wie vor eine Methode oder Variable mit dem Namen "await" haben können, wenn Sie dies möchten. Bis zu einem gewissen Grad denke ich, dass die Verwendung eines neuen Schlüsselworts für neue Funktionen weniger verwirrend ist als die Wiederverwendung eines vorhandenen Schlüsselworts, um mehr als eine Sache zu bedeuten. (übertriebenes Beispiel: dangermouse.net/esoteric/ook.html )
Tim Goodman
5

Wie wäre es, wenn Sie kein Keyword hätten?

Ich möchte, dass der Compiler erkennt, dass ich in den meisten Fällen, wenn ich eine asynchrone Methode aufrufe, das Ergebnis davon haben möchte.

Document doc = DownloadDocumentAsync();

Das ist es. Der Grund, warum die Leute Schwierigkeiten haben, sich ein Schlüsselwort für diese Sache auszudenken, ist, dass es so ist, als hätte man ein Schlüsselwort für "Mach das, was du machen würdest, wenn die Dinge vollkommen normal wären". Dies sollte die Standardeinstellung sein und kein Schlüsselwort erfordern.

Aktualisieren

Ich schlug ursprünglich vor, dass der Compiler mit der Typinferenz klug werden sollte, um herauszufinden, was zu tun ist. Wenn Sie dies weiter überlegen, würde ich die vorhandene Implementierung im CTP beibehalten, aber ein paar triviale Ergänzungen vornehmen, um die Fälle zu reduzieren, in denen Sie das awaitSchlüsselwort explizit verwenden müssten .

Wir erfinden ein Attribut: [AutoAwait]. Dies kann nur auf Methoden angewendet werden. Eine Möglichkeit, dies auf Ihre Methode anzuwenden, besteht darin, es zu markieren async. Sie können es aber auch von Hand machen, zB:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

In einer beliebigen asyncMethode geht der Compiler dann davon aus DownloadDocumentAsync, dass Sie auf einen Aufruf von warten möchten , sodass Sie ihn nicht angeben müssen. Jeder Aufruf dieser Methode wartet automatisch darauf.

Document doc = DownloadDocumentAsync();

Wenn Sie nun "clever" werden und die erhalten möchten Task<Document>, verwenden Sie einen Operator start, der nur vor einem Methodenaufruf angezeigt werden kann:

Task<Document> task = start DownloadDocumentAsync();

Ordentlich, denke ich. Jetzt bedeutet ein einfacher Methodenaufruf, was es normalerweise bedeutet: Warten Sie, bis die Methode abgeschlossen ist. Und startweist auf etwas anderes hin: Warten Sie nicht.

Bei Code, der außerhalb einer asyncMethode angezeigt wird, können Sie eine Methode nur aufrufen, [AutoAwait]indem Sie ihm ein Präfix voranstellen start. Dadurch müssen Sie Code schreiben, der dieselbe Bedeutung hat, unabhängig davon, ob er in einer asyncMethode enthalten ist oder nicht.

Dann werde ich gierig! :)

Erstens möchte ich mich asyncauf Schnittstellenmethoden beziehen:

interface IThing
{
    async int GetCount();
} 

Dies bedeutet im Grunde, dass die implementierende Methode zurückkehren muss Task<int>oder etwas Kompatibles await, und dass Aufrufer der Methode [AutoAwait]Verhalten erhalten.

Auch wenn ich die obige Methode implementiere, möchte ich schreiben können:

async int GetCount()

Task<int>Als Rückgabetyp muss ich also nicht erwähnen .

Außerdem möchte ich mich asyncauf Delegiertypen anwenden (die schließlich wie Schnittstellen mit einer Methode sind). So:

public async delegate TResult AsyncFunc<TResult>();

Ein asyncDelegierter hat - Sie haben es erraten - [AutoAwait]Verhalten. Von einer asyncMethode aus können Sie sie aufrufen und sie wird automatisch awaitbearbeitet (es sei denn, Sie wählen nur startdiese). Und wenn Sie sagen:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

Das funktioniert einfach Es ist kein Methodenaufruf. Es wurde noch keine Aufgabe gestartet - eine async delegateist keine Aufgabe. Es ist eine Fabrik für Aufgaben. Sie können sagen:

Document doc = getDoc();

Damit wird eine Aufgabe gestartet und es wird gewartet, bis sie abgeschlossen ist und Sie das Ergebnis erhalten. Oder du kannst sagen:

Task<Document> t = start getDoc();

Wenn Sie also eine asyncMethode delegieren möchten , müssen Sie wissen, wie man einen async delegateTyp verwendet. Also anstatt Funcdu musst sagen AsyncFunc, und so weiter. Eines Tages könnte dies durch eine verbesserte Typinferenz behoben werden.

Eine andere Frage ist, was passieren soll, wenn Sie sagen, dass Sie mit einer normalen (nicht asynchronen) Methode beginnen. Offensichtlich wäre ein Kompilierungsfehler die sichere Option. Es gibt aber auch andere Möglichkeiten.

Daniel Earwicker
quelle
Dies könnte mit einer impliziten Konvertierung möglich sein, würde aber ansonsten eine Auswertung der Anweisung von links nach rechts erfordern (was genau das Gegenteil der normalen Funktionsweise des Compilers ist, mit Ausnahme von Lambdas). Ich denke, ich würde immer noch dagegen sein, weil es die Verwendung von verhindert var, möglicherweise einen langen expliziten Typnamen ersetzen zu müssen, und auch zwischen dem awaitFall und dem Fall, in dem jemand versehentlich die asynchrone Methode anstelle der normalen synchronen Methode aufgerufen hat, zweideutig ist . Es erscheint zunächst intuitiv, verstößt jedoch gegen das Prinzip der geringsten Überraschung.
Aaronaught
@Aaronaught - Warum verhindert es die Verwendung von var? Ich frage mich, ob Sie auf die vorherige Überarbeitung meiner Antwort antworten ... Ich habe sie komplett umgeschrieben. Sie können sich diesen Vorschlag nun wie folgt vorstellen: Wenn die Methode mit einem speziellen Attribut gekennzeichnet ist, wird das awaitSchlüsselwort automatisch vor den Aufrufen dieser Methode eingefügt (es sei denn, Sie unterdrücken dies mit dem startPräfix). Alles bleibt genau wie im CTP und varfunktioniert daher einwandfrei.
Daniel Earwicker
In der Tat war ich ... seltsam, dass ich mich dazu entschlossen habe, diesen Thread erneut zu besuchen und auf Ihre Antwort fast genau zur gleichen Zeit zu antworten, als Sie beschlossen haben, sie zu bearbeiten. Ich muss es jetzt noch einmal lesen ...
Aaronaught
1
Ich mag Ihre Inversion des Schlüsselworts await. Und ich mag auch nicht die dreifache Redundanz von public async Task<int> FooAsync().
Allon Guralnek
1
Ja, ich sehe diese Async-Postfix-Namenskonvention als Zeichen dafür, dass etwas formeller erfasst werden könnte. Wenn es eine Regel gibt, die besagt, dass "Methoden wie diese auf eine bestimmte Weise benannt werden sollten, damit die Leute wissen, wie man sie richtig aufruft", dann kann dieselbe Regel verwendet werden, um diese Methoden auf eine bestimmte Weise zuzuweisen, und dann der Compiler helfen Ihnen, sie richtig anzurufen.
Daniel Earwicker
4
hearken unto AsyncFetch(…)

(Wenn du es nicht verstehst, lies Erics Blog-Eintrag . Zumindest ist es besser als for sooth Romeo wherefore art thou AsyncFetch(…))

Notiz an mich selbst - denke an einen Namen
quelle
(diejenigen ohne Sinn für Humor müssen nicht zutreffen)
Hinweis an sich selbst - denken Sie an einen Namen
4

Ich denke, asyncist in Ordnung, aber vielleicht liegt das daran, dass ich es mit asynchronen ASP.NET-Seiten verbinde - dieselbe Idee.

Für das awaitKeyword bevorzuge ich continue afteroder resume after.

Ich nicht wie yieldoder eine seiner Varianten, da die Semantik so ist , dass das Verfahren kann nie Ausführung tatsächlich ergibt; es kommt auf den zustand der aufgabe an.

Aaronaught
quelle
Ich mag resume afterfür await. Vielleicht asynckönnte angerufen werden resumable.
Tim Goodman
Obwohl ich es vorziehen würde after, hat der continue afterAnsatz einen starken Implementierungsvorteil: Er enthält ein aktuell vorhandenes kontextbezogenes Schlüsselwort, jedoch mit einer Syntax, die nicht mit der aktuellen Verwendung kompatibel ist. Das garantiert, dass der Zusatz niemals existierenden Code zerstört. Bei der Verwendung eines völlig neuen Schlüsselworts muss die Implementierung mögliche Verwendungen des Wortes als Bezeichner für älteren Code berücksichtigen, was sehr schwierig werden kann.
Edurne Pascual
3

Ich habe auch Kommentare zu Erics Blog hinzugefügt. Ich sehe keine Probleme bei der Verwendung desselben Keywords async

var data = async DownloadFileAsync(url);

Ich drücke nur aus, dass ich die Datei asynchron herunterladen möchte. Hier gibt es ein bisschen Redundanz, "async" erscheint zweimal, weil es auch im Methodennamen steht. Der Compiler könnte besonders clever sein und die Konvention erkennen, dass mit "Async" endende Methoden infakte asynchrone Methoden sind, und diese für uns in den kompilierten Code einfügen. Stattdessen möchten Sie vielleicht einfach anrufen

var data = async DownloadFile(url);

im Gegensatz zu den synchronen zu nennen

var data = DownloadFile(url);

Heck, sollten wir auch in der Lage sein, sie auf die gleiche Weise zu definieren, da das Schlüsselwort async in unserer Deklaration enthalten ist, warum müssen wir jedem Methodennamen manuell "Async" hinzufügen - der Compiler kann dies für uns tun.

Mark H
quelle
Ich mag den Zucker, den Sie hinzugefügt haben, obwohl die C # -Typen wahrscheinlich nicht drauf kommen. (FWIW, bei der Suche nach Attributnamen tun sie bereits etwas Ähnliches.)
Hinweis an sich selbst - denken Sie an einen Namen
2
Und angesichts der asyncTatsache , dass das Schlüsselwort für die Methode nur eine Kleinigkeit ist (wenn ich es richtig verstanden habe), frage ich mich, ob es nicht das Beste wäre, das Gegenteil von dem zu tun, was Sie vorschlagen: asyncdie Methode aufzugeben und sie einfach zu verwenden wo sie derzeit haben await.
Benjol
Das ist eine Möglichkeit. Das asynchrone Schlüsselwort auf der Methode delcaration ist nur für die Sauberkeit wirklich da. Ich würde es vorziehen, wenn es dort bleibt, ohne dass wir den Methodennamen "Async" hinzufügen müssen. ZB async Task<Byte[]> DownloadFile(...)eher als Task<Byte[]> DownloadFileAsync(...)(Letzteres ist sowieso die kompilierte Signatur). So oder so funktioniert.
Mark H
Ich bin ehrlich gesagt auch kein Fan davon. Wie in einem vorherigen Kommentar muss ich darauf hinweisen, dass Ihre endgültige Version gegen das Prinzip der geringsten Überraschung verstößt, da sie tatsächlich eine völlig andere als die geschriebene Methode aufruft und die Implementierung und Signatur dieser Methode der implementierenden Klasse überlassen bleibt . Selbst die erste Version ist problematisch, weil sie nichts aussagt (asynchrone Ausführung dieser asynchronen Methode?). Der Gedanke, den wir zum Ausdruck bringen wollen, ist eine Fortsetzung oder eine verzögerte Ausführung, und das drückt das überhaupt nicht aus.
Aaronaught
3

async = task - Ändert eine Funktion, um eine Aufgabe zurückzugeben. Warum also nicht das Schlüsselwort "task" verwenden?

wait = finish - Wir müssen nicht unbedingt warten, aber die Aufgabe muss "beendet" sein, bevor das Ergebnis verwendet wird.

John Fisher
quelle
Es ist wirklich schwer, mit der Einfachheit hier zu streiten.
blom
2

Ich mag yield until. yield while, bereits vorgeschlagen, ist großartig und führt keine neuen Keywords ein, aber ich denke, "bis" erfasst das Verhalten ein wenig besser.

Ich denke, das yield <something>ist eine großartige Idee, denn Yield fängt bereits die Idee ein, den Rest der Methode so gut fortzusetzen. Vielleicht kann sich jemand ein besseres Wort als "bis" vorstellen.

Nlawalker
quelle
2

Ich möchte nur meine Stimme für den Vorschlag von Aaron G registrieren comefrom- die erste angemessene Verwendung, die ich von der COMEFROM- Erklärung von INTERCAL gesehen habe . Die Idee ist, dass es das Gegenteil von GOTO ist ( von der GOTO-Anweisung wegzuspringen ), indem es einen Platz in Ihrem Code schafft , zur COMEFROM-Anweisung zu springen.

Gabe
quelle
2

Task<T>Wie wäre es, startwenn Sie, da es sich um s handelt, als Schlüsselwort vor der Anweisung Folgendes verwenden:

start var document = FetchAsync(urls[i]);

Protagonist
quelle
Hmmm, finishwäre vielleicht noch besser als start?
Protagonist
1

Es ist erwähnenswert, dass F # das asyncSchlüsselwort auch in seinen asynchronen Workflows verwendet, was ziemlich genau der neuen asynchronen Funktionalität in C # 5 entspricht. Deshalb würde ich das auch so lassen

Für das awaitSchlüsselwort in F # verwenden sie einfach let!anstelle von let. C # hat nicht die gleiche Zuweisungssyntax, daher benötigen sie etwas auf der rechten Seite des =Zeichens. Wie Benjol sagte, funktioniert es genauso wie yieldes fast eine Variante davon sein sollte.

Scott Whitlock
quelle
1
Ein "Warten" muss überhaupt nicht in einer Anweisung enthalten sein (obwohl dies normalerweise der Fall ist). Als Operator ist es für so ziemlich jeden Ausdruck zulässig, der einen Typ hat, für den wir einen GetAwaiter finden können. (Die genauen Regeln müssen noch in eine veröffentlichbare Form gebracht werden.)
Eric Lippert
1
@Eric, in F # wäre das do!, aber du wusstest, dass ...
Benjol
1

yield async FetchAsync(..)


Dies passt perfekt zu dem asyncModifikator, den Sie für die aufgerufene Methode benötigen. Und auch die Semantik der aktuell , yield returndas heißt, Sie zurückkommen und Ausbeuten Ausführung an das Aufzählen Code während in diesem Fall sind Sie Ihre Ausführung der asynchronen Methode ergeben.

Stellen Sie sich vor, wenn es in Zukunft andere Verwendungszwecke für geben sollte yield, könnten wir ein " yield xwhere x" als neue glänzende Funktion hinzufügen, anstatt all diese verschiedenen Schlüsselwörter zu haben, um zumeist das Gleiche zu tun und die Ausführung zu ermöglichen.

Ehrlich gesagt verstehe ich das Argument, dass die Hinrichtung nicht möglich ist, nicht ganz. Ist der Sinn des Aufrufs einer anderen Methode nicht bereits, diese Methode auszuführen? Egal ob asynchron oder nicht? Ich vermisse etwas hier?

Und gut für Sie, wenn die asyncRückgabe synchron erfolgt, das Schlüsselwort jedoch vorhanden ist, um anzuzeigen, dass die Methode wahrscheinlich asynchron ausgeführt wird und Sie die Ausführung für eine andere Methode ausgeben. Ihre Methode sollte dies berücksichtigen, unabhängig davon, ob die Methode tatsächlich asynchrone Aufrufe ausführt oder nicht.

IMO Ich denke, dass die verschiedenen Fälle, in denen keine Ergebnisse erzielt werden, ein Implementierungsdetail sind. Ich würde lieber für Konsistenz in der Sprache bürgen (dh wiederverwenden yield).

Chakrit
quelle
0

Wie wäre es mit complete"Ich möchte, dass die Aufgabe erledigt wird"?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;
Allon Guralnek
quelle
1
Warum die Gegenstimme? Es ist eine Höflichkeit, sich nach dem Abstimmen wenigstens zu erklären.
Allon Guralnek