Frage

Ich habe im Anschluss an die neue Ankündigung in Bezug auf die neue async-Funktion, die in c # 5.0 sein wird. Ich habe ein grundlegendes Verständnis der Fortsetzung vorbei Stil und der Transformation der neuen C # Compiler macht Code wie dieser Ausschnitt aus Eric Lippert Post :

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);
  }
}

Ich weiß, dass einige Sprachen Fortsetzungen nativ implementieren, über Call-mit-Strom-Fortsetzung (callcc), aber ich weiß nicht wirklich verstehen, wie das funktioniert oder was es tut genau das.

Also hier ist die Frage: Wenn Anders et al. beschlossen hatte, den sauren Apfel beißen und nur callcc in c # 5.0 anstelle des async / await Sonderfall, was würde das obige Snippet aussehen wie implementieren?

War es hilfreich?

Lösung

Original Antwort:

Ihre Frage, wie ich es verstehe, ist „was ist, wenn statt der Umsetzung‚await‘speziell für aufgabenbasierte Asynchronität vielmehr der allgemeinere Steuerfluss Betrieb von Call-mit-Stromfortsetzungs umgesetzt worden?“

Nun, denken in erster Linie reden wir über was „await“ der Fall ist. „Await“ nimmt einen Ausdruck vom Typ Task<T>, erhält eine Erwartenden, und ruft die Erwartenden mit der aktuellen Fortsetzung:

await FooAsync()

wird effektiv

var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);

Wir nehmen nun an einen Operator callcc hatten, die als Argument eine Methode, und ruft die Methode mit der aktuellen Fortsetzung. Das würde wie folgt aussehen:

var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;

Mit anderen Worten:

await FooAsync()

ist nichts anderes als

callcc FooAsync().GetAwaiter().BeginAwait;

Heißt das, Ihre Frage zu beantworten?


Update # 1:

Wie ein Kommentator weist darauf hin, nimmt die Antwort unter der Codegenerierung Muster aus der „Technology Preview“ -Version der async / await Funktion. Wir generieren tatsächlich etwas anderen Code in der Beta-Version des Merkmals, obwohl logisch es ist das gleiche. Die vorliegenden codegen sind so etwas wie:

var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
    awaiter.OnCompleted(somehow get the current continuation);
    // control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.

Beachten Sie, dass dies ist etwas komplizierter, und die Griffe der Fall, wenn Sie „erwarten“ ein Ergebnis sind die bereits berechnet hat. Es gibt keine Notwendigkeit, durch alle rigamarole gehen Steuerung an den Aufrufer des Nachgebens und wieder anziehende, wo Sie aufgehört haben, wenn das Ergebnis, dass Sie warten in der Tat bereits im Arbeitsspeicher zwischengespeichert für Sie da.

So ist die Verbindung zwischen „await“ und „callcc“ ist nicht ganz so einfach, wie es in der Preview-Version, aber es ist immer noch klar, dass wir im Wesentlichen eine callcc auf der „OnCompleted“ Methode des Erwartenden tun. Wir haben einfach nicht die callcc tun, wenn wir nicht haben.


Update # 2:

Wie diese Antwort

https://stackoverflow.com/a/9826822/88656

von Timwi weist darauf hin, die Semantik von Call / cc und await sind nicht ganz dasselbe; ein „true“ Anruf / cc erfordert entweder, dass wir „capture“ die gesamten Fortsetzung eines Verfahrens einschließlich seiner gesamten Call-Stack , oder äquivalent, dass das ganze Programm in Fortsetzung neu geschrieben werden vorbei Stil.

Der „await“ -Funktion ist eher wie ein „kooperativer Anruf / cc“; die Fortsetzung nur erfaßt „was der Strom Task-Verfahren zum Zurückkehren über die nächsten an der Stelle des await zu tun?“ Wenn die Anrufer von dem Task-Verfahren zum Zurückkehren wird etwas Interessantes tun, nachdem die Aufgabe abgeschlossen ist, dann ist es kostenlos registrieren seine Fortsetzung als die Fortsetzung der Aufgabe.

Andere Tipps

Ich bin kein Experte auf Fortsetzungen, aber ich werde einen Stich nehmen zu erklären den Unterschied zwischen Asynchron / await und Call / cc. Natürlich nimmt diese Erklärung, die ich Anruf- / cc und Asynchron / await verstehen, was ich nicht sicher bin, tue ich. Dennoch, hier geht ...

Mit C # ‚async‘, erzählen Sie die Compiler eine spezielle Version dieser speziellen Methode zu erzeugen, die versteht, wie es den Zustand in eine heap-Datenstruktur Flasche, so kann er „aus den realen Stapeln entfernt“ werden, und später wieder aufgenommen. Innerhalb eines Asynchron-Kontext „await“ ist dann wie „call / cc“, dass es verwendet die Compiler generierten Objekte, um den Zustand zu Flasche und den „echten Stapel“ aus, bis die Aufgabe beendet. Da es jedoch der Compiler ist eine Asynchron-Methode umschreiben, die den Zustand erlaubt Flaschen abgefüllt werden kann await nur innerhalb eines Asynchron-Kontextes verwendet werden.

In First-Class-Ruf / cc erzeugt die Language Runtime den gesamten Code, so dass die aktuelle Fortsetzung eines Gespräches-Continuation-Funktion kann in Flaschen abgefüllt in (was die Asynchron Schlüsselwort unnötig). Ruf- / cc wirkt immer noch wie Await, wodurch der aktuelle Fortsetzungszustand (man denke an die Stack-Zustand) in an die angerufene-Funktion als eine Funktion botled auf und übergeben werden. Eine Möglichkeit, dies zu tun ist, heap-Frames zu verwenden, für alle Funktionsaufruf, anstelle von ‚Stack‘ Frames. (Manchmal als ‚stackless‘, wie in ‚stackless Python‘ oder viele Schema-Implementierungen) Eine andere Möglichkeit ist es, alle die Daten aus dem „echten Stapel“ zu entfernen und stopfen es in einen Haufen Daten-Struktur, bevor der Anruf / cc Ziel Aufruf .

können einige knifflige Probleme entstehen, wenn es Anrufe an externe Funktionen (man denke DllImport) auf dem Stapel miteinander vermischt. Ich vermute, dies ist der Grund, sie mit der Asynchron / await Umsetzung ging.

http://www.madore.org/~david/computers/callcc. html


Da in C #, eine Funktion als „async“ gekennzeichnet werden, damit muß diese Mechanik zu verwenden, ich frage mich, ob dieses async Schlüsselwort einen Virus, dass die Spreads auf viele Funktionen in vielen Bibliotheken werden wird. Wenn das passiert. schließlich erkennen, dass sie können, sollten sie erstklassigen Ruf / cc auf der VM-Ebene implementieren, statt dieser Compiler-Rewriting Asynchron-Modell. Nur die Zeit kann es verraten. Aber es ist sicherlich ein nützliches Werkzeug im Zusammenhang mit der aktuellen C # Umgebung.

Kinda sinnlos Ich würde sagen. Scheme Dolmetscher oft Anruf / cc mit einer Zustandsmaschine, dass Captures lokaler Zustand auf den Heap implementieren. Welche ist genau was C # 5.0 (oder richtiger C # 2.0 des Iterators) als auch der Fall ist. Sie haben Anruf / cc implementieren, die Abstraktion sie kam ziemlich elegant.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top