Frage

Was sind sie und wofür sind sie gut?

Ich habe keinen CS-Abschluss und mein Hintergrund ist VB6 -> ASP -> ASP.NET/C#.Kann es jemand klar und prägnant erklären?

War es hilfreich?

Lösung

Stellen Sie sich vor, jede einzelne Zeile in Ihrem Programm wäre eine separate Funktion.Jeder akzeptiert als Parameter die nächste auszuführende Zeile/Funktion.

Mit diesem Modell können Sie die Ausführung an jeder Zeile „anhalten“ und später fortsetzen.Sie können auch einfallsreiche Dinge tun, wie zum Beispiel vorübergehend den Ausführungsstapel hochspringen, um einen Wert abzurufen, oder den aktuellen Ausführungsstatus in einer Datenbank speichern, um ihn später abzurufen.

Andere Tipps

Sie verstehen sie wahrscheinlich besser, als Sie denken.

Ausnahmen sind ein Beispiel für „nur nach oben gerichtete“ Fortsetzungen.Sie ermöglichen es Code tief unten im Stapel, einen Ausnahmehandler aufzurufen, um auf ein Problem hinzuweisen.

Python-Beispiel:

try:
    broken_function()
except SomeException:
    # jump to here
    pass

def broken_function():
    raise SomeException() # go back up the stack
    # stuff that won't be evaluated

Generatoren sind Beispiele für „nur nach unten gerichtete“ Fortsetzungen.Sie ermöglichen es dem Code, erneut in eine Schleife einzutreten, um beispielsweise neue Werte zu erstellen.

Python-Beispiel:

def sequence_generator(i=1):
    while True:
        yield i  # "return" this value, and come back here for the next
        i = i + 1

g = sequence_generator()
while True:
    print g.next()

In beiden Fällen mussten diese speziell zur Sprache hinzugefügt werden, während der Programmierer in einer Sprache mit Fortsetzungen diese Dinge dort erstellen kann, wo sie nicht verfügbar sind.

Achtung: Dieses Beispiel ist weder prägnant noch besonders klar.Dies ist ein Beweis für die wirkungsvolle Anwendung von Fortsetzungen.Als VB/ASP/C#-Programmierer sind Sie möglicherweise nicht mit dem Konzept eines Systemstapels oder Speicherstatus vertraut, daher ist das Ziel dieser Antwort eine Demonstration und keine Erklärung.

Fortsetzungen sind äußerst vielseitig und bieten eine Möglichkeit, den Ausführungsstatus zu speichern und später fortzusetzen.Hier ist ein kleines Beispiel einer kooperativen Multithreading-Umgebung, die Fortsetzungen in Scheme verwendet:

(Gehen Sie davon aus, dass die Vorgänge „enqueue“ und „dequeue“ in einer hier nicht definierten globalen Warteschlange wie erwartet funktionieren.)

(define (fork)
  (display "forking\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue (lambda ()
                (cc #f)))
     (cc #t))))

(define (context-switch)
  (display "context switching\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue
      (lambda ()
        (cc 'nothing)))
     ((dequeue)))))

(define (end-process)
  (display "ending process\n")
  (let ((proc (dequeue)))
    (if (eq? proc 'queue-empty)
        (display "all processes terminated\n")
        (proc))))

Dies stellt drei Verben bereit, die eine Funktion verwenden kann: fork, context-switch und end-process.Die fork-Operation verzweigt den Thread und gibt in einer Instanz #t und in einer anderen #f zurück.Der Kontextwechselvorgang wechselt zwischen Threads und der Endprozess beendet einen Thread.

Hier ist ein Beispiel für ihre Verwendung:

(define (test-cs)
  (display "entering test\n")
  (cond
    ((fork) (cond
              ((fork) (display "process 1\n")
                      (context-switch)
                      (display "process 1 again\n"))
              (else (display "process 2\n")
                    (end-process)
                    (display "you shouldn't see this (2)"))))
    (else (cond ((fork) (display "process 3\n")
                        (display "process 3 again\n")
                        (context-switch))
                (else (display "process 4\n")))))
  (context-switch)
  (display "ending process\n")
  (end-process)
  (display "process ended (should only see this once)\n"))

Die Ausgabe sollte sein

entering test
forking
forking
process 1
context switching
forking
process 3
process 3 again
context switching
process 2
ending process
process 1 again
context switching
process 4
context switching
context switching
ending process
ending process
ending process
ending process
ending process
ending process
all processes terminated
process ended (should only see this once)

Diejenigen, die sich in einem Kurs mit Forking und Threading beschäftigt haben, erhalten oft ähnliche Beispiele.Der Zweck dieses Beitrags besteht darin, zu zeigen, dass Sie mit Fortsetzungen ähnliche Ergebnisse innerhalb eines einzelnen Threads erzielen können, indem Sie dessen Status – seine Fortsetzung – manuell speichern und wiederherstellen.

P.S.– Ich glaube, ich erinnere mich an etwas Ähnliches in „On Lisp“. Wenn Sie also professionellen Code sehen möchten, sollten Sie sich das Buch ansehen.

Eine Fortsetzung kann man sich beispielsweise als Prozessorstapel vorstellen.Wenn Sie „call-with-current-continuation c“ aufrufen, ruft es Ihre Funktion „c“ auf und der an „c“ übergebene Parameter ist Ihr aktueller Stapel mit all Ihren automatischen Variablen darauf (dargestellt als eine weitere Funktion, nennen Sie sie „k“) ").Währenddessen beginnt der Prozessor mit der Erstellung eines neuen Stapels.Wenn Sie „k“ aufrufen, führt es eine „Return from Subroutine“ (RTS)-Anweisung auf dem ursprünglichen Stapel aus und bringt Sie zurück in den Kontext des ursprünglichen „call-with-current-continuation“ („call-cc“ von jetzt an). eingeschaltet) und Ihr Programm kann wie zuvor fortgesetzt werden.Wenn Sie einen Parameter an „k“ übergeben haben, wird dieser zum Rückgabewert von „call-cc“.

Aus der Sicht Ihres ursprünglichen Stacks sieht „call-cc“ wie ein normaler Funktionsaufruf aus.Aus der Sicht von „c“ sieht Ihr ursprünglicher Stapel wie eine Funktion aus, die niemals zurückkehrt.

Es gibt einen alten Witz über einen Mathematiker, der einen Löwen in einem Käfig gefangen hat, indem er in den Käfig kletterte, ihn abschloss und erklärte, er sei außerhalb des Käfigs, während sich alles andere (einschließlich des Löwen) darin befände.Fortsetzungen sind ein bisschen wie der Käfig und „c“ ist ein bisschen wie der Mathematiker.Ihr Hauptprogramm geht davon aus, dass „c“ darin enthalten ist, während „c“ davon ausgeht, dass sich Ihr Hauptprogramm in „k“ befindet.

Mithilfe von Fortsetzungen können Sie beliebige Kontrollflussstrukturen erstellen.Sie können beispielsweise eine Threading-Bibliothek erstellen.„yield“ verwendet „call-cc“, um die aktuelle Fortsetzung in eine Warteschlange zu stellen und springt dann zu der Fortsetzung am Anfang der Warteschlange.Ein Semaphor verfügt auch über eine eigene Warteschlange angehaltener Fortsetzungen, und ein Thread wird neu geplant, indem er aus der Semaphor-Warteschlange entfernt und in die Hauptwarteschlange gestellt wird.

Grundsätzlich ist eine Fortsetzung die Fähigkeit einer Funktion, die Ausführung zu stoppen und zu einem späteren Zeitpunkt dort fortzufahren, wo sie aufgehört hat.In C# können Sie dies mit dem Schlüsselwort yield tun.Wenn Sie möchten, kann ich näher darauf eingehen, aber Sie wollten eine prägnante Erklärung.;-)

Ich bin immer noch dabei, mich an Fortsetzungen zu „gewöhnen“, aber eine Möglichkeit, über sie nachzudenken, die ich nützlich finde, ist die Abstraktion des Program Counter (PC)-Konzepts.Ein PC „zeigt“ auf den nächsten Befehl, der im Speicher ausgeführt werden soll, aber natürlich verweist dieser Befehl (und so ziemlich jeder Befehl) implizit oder explizit auf den folgenden Befehl sowie auf alle Befehle, die Interrupts bedienen sollen.(Sogar eine NOOP-Anweisung führt implizit einen JUMP zur nächsten Anweisung im Speicher durch.Wenn jedoch ein Interrupt auftritt, führt dies normalerweise zu einem JUMP zu einer anderen Anweisung im Speicher.)

Jeder potenziell „lebende“ Punkt in einem Programm im Speicher, zu dem die Steuerung an einem bestimmten Punkt springen könnte, ist gewissermaßen eine aktive Fortsetzung.Andere Punkte, die erreicht werden können, sind potenziell aktive Fortsetzungen, aber genauer gesagt handelt es sich um Fortsetzungen, die potenziell (vielleicht dynamisch) als Ergebnis des Erreichens einer oder mehrerer der derzeit aktiven Fortsetzungen „berechnet“ werden.

Dies scheint in traditionellen Einführungen in Fortsetzungen, in denen alle ausstehenden Ausführungsthreads explizit als Fortsetzungen in statischem Code dargestellt werden, etwas fehl am Platz zu sein;Es berücksichtigt jedoch die Tatsache, dass der PC auf Allzweckcomputern auf eine Befehlssequenz verweist, die möglicherweise den Speicherinhalt ändert, der einen Teil dieser Befehlssequenz darstellt, und so im Wesentlichen eine neue (oder, wenn Sie so wollen, geänderte) erstellt ) Fortsetzung im laufenden Betrieb, die zum Zeitpunkt der Aktivierung von Fortsetzungen vor dieser Erstellung/Änderung nicht wirklich existiert.

Die Fortsetzung kann also als High-Level-Modell des PCs betrachtet werden, weshalb sie konzeptionell den gewöhnlichen Prozeduraufruf/-rücksprung umfasst (so wie das alte Eisen den Prozeduraufruf/-rückkehr über Low-Level-JUMP, auch bekannt als GOTO, Anweisungen plus Aufzeichnung der PC bei Abruf und Wiederherstellung bei Rückkehr) sowie Ausnahmen, Threads, Coroutinen usw.

So wie der PC darauf hinweist, dass Berechnungen in „der Zukunft“ stattfinden werden, bewirkt eine Fortsetzung dasselbe, jedoch auf einer höheren, abstrakteren Ebene.Der PC bezieht sich implizit auf den Speicher plus alle Speicherorte und Register, die an beliebige Werte „gebunden“ sind, während eine Fortsetzung die Zukunft über die sprachgerechten Abstraktionen darstellt.

Obwohl es normalerweise nur einen PC pro Computer (Kernprozessor) gibt, gibt es natürlich, wie oben erwähnt, tatsächlich viele „aktive“ PC-ähnliche Einheiten.Der Interrupt-Vektor enthält eine Menge, der Stapel eine Menge mehr, bestimmte Register könnten einige enthalten usw.Sie werden „aktiviert“, wenn ihre Werte in den Hardware-PC geladen werden, aber Fortsetzungen sind Abstraktionen des Konzepts, keine PCs oder ihr genaues Äquivalent (es gibt kein inhärentes Konzept einer „Master“-Fortsetzung, obwohl wir oft in diesen Begriffen denken und programmieren um die Dinge einigermaßen einfach zu halten).

Im Wesentlichen ist eine Fortsetzung eine Darstellung dessen, „was als nächstes zu tun ist, wenn sie aufgerufen wird“ und kann als solche ein erstklassiges Objekt sein (und ist es in manchen Sprachen und in Programmen im Continuation-Passing-Stil oft auch). werden wie die meisten anderen Datentypen instanziiert, weitergegeben und verworfen, und zwar ähnlich wie ein klassischer Computer mit Speicherorten umgeht Vis-a-Vis der PC – nahezu austauschbar mit gewöhnlichen ganzen Zahlen.

In C# haben Sie Zugriff auf zwei Fortsetzungen.Eins, Zugriff über return, lässt eine Methode dort weitermachen, wo sie aufgerufen wurde.Der andere, zugänglich über throw, lässt eine Methode bei der nächsten Übereinstimmung fortfahren catch.

In einigen Sprachen können Sie diese Anweisungen als erstklassige Werte behandeln, sodass Sie sie zuweisen und in Variablen weitergeben können.Das bedeutet, dass Sie den Wert verstecken können return Oder von throw und rufen Sie sie später an, wenn Sie es sind Wirklich bereit zur Rückgabe oder zum Werfen.

Continuation callback = return;
callMeLater(callback);

Das kann in vielen Situationen praktisch sein.Ein Beispiel ist wie oben: Sie möchten Ihre Arbeit unterbrechen und später wieder aufnehmen, wenn etwas passiert (z. B. eine Webanfrage oder ähnliches).

Ich verwende sie in einigen Projekten, an denen ich arbeite.In einem Fall verwende ich sie, damit ich das Programm anhalten kann, während ich auf E/A über das Netzwerk warte, und es später wieder fortsetzen kann.Im anderen Fall schreibe ich eine Programmiersprache, in der ich dem Benutzer Zugriff auf Fortsetzungen als Werte gebe, damit er schreiben kann return Und throw für sich selbst - oder einen anderen Kontrollfluss, wie while Schleifen - ohne dass ich es für sie tun muss.

Denken Sie an Threads.Ein Thread kann ausgeführt werden und Sie können das Ergebnis seiner Berechnung erhalten.Eine Fortsetzung ist ein Thread, den Sie kopieren können, sodass Sie dieselbe Berechnung zweimal ausführen können.

Fortsetzungen haben bei der Webprogrammierung erneut Interesse geweckt, da sie den Pausen-/Fortsetzungscharakter von Webanfragen gut widerspiegeln.Ein Server kann eine Fortsetzung erstellen, die die Sitzung eines Benutzers darstellt, und fortfahren, wenn der Benutzer die Sitzung fortsetzt.

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