
Jede Sprache, die paralleles (wettbewerbsfähiges, asynchrones) Computing unterstützt, benötigt eine Möglichkeit, Code parallel auszuführen. Hier sind Beispiele aus verschiedenen APIs:
go myfunc(); // Golang pthread_create(&thread_id, NULL, &myfunc); /* C with POSIX threads */ spawn(modulename, myfuncname, []) % Erlang threading.Thread(target=myfunc).start() # Python with threads asyncio.create_task(myfunc()) # Python with asyncio
Es gibt viele Optionen für Notation und Terminologie, aber eine Semantik besteht darin, myfunc
parallel zum Hauptprogramm myfunc
und den übergeordneten Ausführungsthread (Kontrollfluss) fortzusetzen.
Eine andere Option ist Rückrufe :
QObject::connect(&emitter, SIGNAL(event()), // C++ with Qt &receiver, SLOT(myfunc())) g_signal_connect(emitter, "event", myfunc, NULL) /* C with GObject */ document.getElementById("myid").onclick = myfunc; // Javascript promise.then(myfunc, errorhandler) // Javascript with Promises deferred.addCallback(myfunc) # Python with Twisted future.add_done_callback(myfunc) # Python with asyncio
Und wieder ändert sich die Notation, aber alle Beispiele machen es so, dass myfunc
vom aktuellen Moment an startet, wenn und wenn ein bestimmtes Ereignis myfunc
. Sobald der Rückruf eingestellt ist, kehrt die Steuerung zurück und die aufrufende Funktion wird fortgesetzt. (Manchmal werden Rückrufe in praktische Kombinationsfunktionen oder Protokolle im Twisted-Stil eingebunden , aber die Grundidee bleibt unverändert.)
Und das ist alles. Nehmen Sie eine gängige Mehrzwecksprache, und Sie werden wahrscheinlich feststellen, dass sie in eines dieser Paradigmen fällt (manchmal beides, asyncio).
Aber nicht meine neue seltsame Trio- Bibliothek. Sie verwendet diese Ansätze nicht. Wenn wir stattdessen myfunc
und anotherfunc
parallel myfunc
anotherfunc
, schreiben wir anotherfunc
:
async with trio.open_nursery() as nursery: nursery.start_soon(myfunc) nursery.start_soon(anotherfunc)
Kindergarten - Kindergarten, Kindergarten
Zum ersten Mal sind die Menschen vor dem Hintergrund der Gestaltung des "Kindergartens" verloren. Warum gibt es einen Kontextmanager (mit Block)? Was ist das für ein Kindergarten und warum ist es notwendig, eine Aufgabe auszuführen? Dann verstehen die Leute, dass der Kindergarten die üblichen Ansätze in anderen Rahmen stört und wütend wird. Alles wirkt bizarr, spezifisch und zu hoch, um ein grundlegendes Primitiv zu sein. All dies sind verständliche Reaktionen! Aber ertrage es ein wenig.
In diesem Artikel möchte ich Sie davon überzeugen, dass Kindergärten keine Modeerscheinung sind, sondern ein neues Grundelement für die Steuerung des Ausführungsflusses, so grundlegend wie Schleifen und Funktionsaufrufe. Darüber hinaus müssen die oben diskutierten Ansätze (Erstellen von Threads und Registrieren von Rückrufen) verworfen und durch Kindergärten ersetzt werden.
Klingt zu fett? Dies ist jedoch bereits geschehen: goto
häufig verwendet, um das Verhalten eines Programms zu steuern. Dies ist eine Gelegenheit zum Lachen:

Einige Sprachen haben noch das sogenannte goto
, aber seine Fähigkeiten sind viel eingeschränkter als das ursprüngliche goto
. Und in den meisten Sprachen ist es überhaupt nicht. Was ist mit ihm passiert? Diese Geschichte ist überraschend aktuell, obwohl sie den meisten wegen ihrer Antike unbekannt ist. Erinnern wir uns daran, was goto
und wie dies bei der asynchronen Programmierung helfen kann.
Inhaltsverzeichnis
- Was ist goto?
- Was ist los?
- Was ist mit goto passiert?
- goto zerstört die Abstraktion
- Schöne neue Welt ohne zu gehen
- Nicht mehr gehen
- Über die Gefahren von Ausdrücken vom Typ „Go“
- go-Ausdrücke brechen Abstraktionen.
- go-expressions unterbrechen die automatische Bereinigung offener Ressourcen.
- go-Ausdrücke unterbrechen die Fehlerbehandlung.
- Nicht mehr gehen
- Kindergarten als baulicher Ersatz für unterwegs
- Der Kindergarten behält die Abstraktion der Funktionen.
- Der Kindergarten unterstützt das dynamische Hinzufügen von Aufgaben.
- Sie können den Kindergarten trotzdem verlassen.
- Sie können neue Typen identifizieren, die als Kindergarten quaken.
- Nein, Kindergärten warten immer darauf, dass alle Aufgaben erledigt sind.
- Funktioniert die automatische Reinigung von Ressourcen.
- Fehlererhebung funktioniert.
- Schöne neue Welt ohne gehen
- Kindergärten in der Praxis
- Schlussfolgerungen
- Kommentare
- Danksagung
- Fußnoten
- Über den Autor
- Fortsetzung
Was ist goto
?
Die ersten Computer wurden mit Assembler oder noch primitiver programmiert. Das ist nicht sehr praktisch. In den 1950er Jahren begannen Leute wie John Backus von IBM und Grace Hopper von Remington Rand Sprachen wie FORTRAN und FLOW-MATIC (besser bekannt für seinen direkten Nachkommen COBOL ) zu entwickeln.
FLOW-MATIC war zu dieser Zeit sehr ehrgeizig. Sie können es sich als den Ur-Ur-Ur-Ur-Großvater von Python vorstellen - es war die erste Sprache, die hauptsächlich für Menschen entwickelt wurde, und die zweite für Computer. Er sah so aus:

Beachten Sie, dass es im Gegensatz zu modernen Sprachen keine Bedingungen für Blöcke, Schleifen oder Funktionsaufrufe gibt - tatsächlich gibt es überhaupt keine Blöcke oder Einrückungen. Dies ist nur eine sequentielle Liste von Ausdrücken. Nicht, weil dieses Programm zu kurz ist, um Steueranweisungen (außer JUMP TO
) zu erfordern - eine solche Syntax wurde noch nicht erfunden!

Stattdessen hatte FLOW-MATIC zwei Möglichkeiten, den Ausführungsfluss zu steuern. Normalerweise war der Fluss konsistent - beginnen Sie von oben und bewegen Sie sich nach unten, jeweils einen Ausdruck nach dem anderen. Wenn Sie jedoch den speziellen Ausdruck JUMP TO
ausführen, kann er an anderer Stelle die Steuerung übernehmen. Zum Beispiel springt Ausdruck (13) zu Ausdruck (2):

Genau wie bei den Grundelementen der Parallelität vom Anfang des Artikels gab es keine Einigung darüber, wie diese "Einwegsprung" -Operation zu bezeichnen ist. In der Auflistung ist dies JUMP TO
, aber goto
historisch Wurzeln geschlagen (wie "go there"), die ich hier verwende.
Hier ist der komplette Satz von Sprüngen in diesem kleinen Programm:

Das kommt dir nicht nur verwirrend vor! FLOW-MATIC hat diesen springbasierten Programmierstil direkt vom Assembler geerbt. Es ist leistungsfähig und entspricht in etwa der tatsächlichen Funktionsweise der Computerhardware, es ist jedoch sehr schwierig, direkt damit zu arbeiten. Diese Kugel aus Pfeilen ist der Grund für die Erfindung des Begriffs "Spaghetti-Code".
Aber warum hat das so ein Problem verursacht? Warum sind einige Steueranweisungen gut und andere nicht? Wie wählt man die Guten aus? Zu dieser Zeit war es völlig unverständlich, und wenn Sie das Problem nicht verstehen, ist es schwer zu lösen.
Was ist go
?
Lassen Sie uns von unserer Geschichte abschweifen. Jeder weiß, dass goto
schlecht war, aber was hat das mit Asynchronität zu tun? Schauen Sie sich den berühmten go
Ausdruck aus Golang an, mit dem der neue "Goroutine" (Lightweight Stream) erzeugt wird:
// Golang go myfunc();
Ist es möglich, ein Diagramm seines Ausführungsflusses zu zeichnen? Es unterscheidet sich geringfügig von dem obigen Diagramm, da hier der Stream aufgeteilt ist. Lassen Sie es uns so zeichnen:

Die Farben hier sollen zeigen, dass beide Pfade gewählt sind. Aus Sicht der übergeordneten Goroutine (grüne Linie) wird der Kontrollfluss sequentiell ausgeführt: Er beginnt von oben und geht dann sofort nach unten. Aus Sicht der Nachkommenfunktion (Fliederlinie) kommt der Strom myfunc
von oben und springt dann in den Körper von myfunc
. Im Gegensatz zu einem regulären Funktionsaufruf gibt es einen Sprung in eine Richtung - beim Starten von myfunc
wechseln wir zu einem völlig neuen Stack und die Laufzeit vergisst sofort, woher wir gekommen sind.
anscheinend meine ich den callstack
Dies gilt aber nicht nur für Golang. Dieses Diagramm gilt für alle am Anfang des Artikels aufgeführten Grundelemente (Steuerelemente):
- Threading-Bibliotheken geben normalerweise eine Art Steuerobjekt zurück, mit dem sie später dem Thread beitreten können. Dies ist jedoch eine unabhängige Operation, von der die Sprache selbst nichts weiß. Das Grundelement zum Erstellen eines neuen Threads hat das oben gezeigte Diagramm.
- Die Registrierung von Rückrufen entspricht semantisch der Erstellung eines Hintergrundthreads (obwohl es offensichtlich ist, dass die Implementierung unterschiedlich ist), der:
a) gesperrt wird, bis ein Ereignis eintritt, und dann
b) startet eine Rückruffunktion
Rückrufregistrierung ist also in Bezug auf übergeordnete Steueroperatoren ein Ausdruck, der mit go
identisch ist. - Bei
Futures
und Promises
dasselbe: Wenn Sie die Funktion ausführen und Promise
, bedeutet dies, dass die Arbeit im Hintergrund geplant ist und ein Kontrollobjekt zurückgegeben wird, um das Ergebnis später zu erhalten (falls Sie dies wünschen). Aus Sicht der Management-Semantik entspricht dies dem Erstellen eines Flows. Danach übergeben Sie den Rückruf an Promis und dann wie im vorherigen Absatz.
Dasselbe Muster zeigt sich in vielen Formen - die wesentliche Ähnlichkeit besteht darin, dass in all diesen Fällen der Kontrollfluss aufgeteilt wird - es wird ein Sprung zum neuen Thread ausgeführt, aber der übergeordnete Thread kehrt zu dem zurück, der ihn aufgerufen hat. Wenn Sie wissen, was Sie sehen müssen, werden Sie es überall sehen! Dies ist ein interessantes Spiel (zumindest für einige Arten von Menschen)!
Trotzdem ärgert es mich, dass es für diese Kategorie von Steueranweisungen keinen Standardnamen gibt. Ich benutze den Ausdruck "go" , um sie aufzurufen, so wie "goto" ein Oberbegriff für alle goto
Ausdrücke geworden ist. Warum go
? Ein Grund dafür ist, dass Golang uns ein sehr sauberes Beispiel für eine solche Syntax gibt. Und der andere ist:

Beachten Sie die Ähnlichkeit? Das ist richtig - go
ist eine der goto
Formen.
Asynchrone Programme sind für ihre Schwierigkeit beim Schreiben und Analysieren berüchtigt. Sowie Programme basierend auf goto
. Die durch goto
verursachten Probleme goto
meist in modernen Sprachen gelöst. Wenn wir lernen, wie man goto
behebt, hilft dies, bequemere asynchrone APIs zu erstellen? Lass es uns herausfinden!
Was ist mit goto
passiert?
Was ist also los mit goto
, das so viele Probleme verursacht? In den späten 60er Jahren hat Edsger Wieb Dijkstra einige bekannte Werke geschrieben, die dazu beigetragen haben, dies besser zu verstehen: Die Argumente gegen den goto-Operator und Hinweise zur strukturellen Programmierung .
goto
zerstört die Abstraktion
In diesen Arbeiten hat sich Dijkstra Gedanken darüber gemacht, wie wir nicht-triviale Programme schreiben und deren Richtigkeit sicherstellen. Es gibt viele interessante Punkte. Zum Beispiel haben Sie wahrscheinlich diesen Satz gehört:
Testprogramme können das Vorhandensein von Fehlern anzeigen, jedoch niemals deren Abwesenheit.
Ja, dies stammt aus Structural Programming Notes . Sein Hauptanliegen war jedoch die Abstraktion . Er wollte Programme schreiben, die zu groß waren, um sie im Kopf zu behalten. Dazu müssen Sie die Programmteile als Black Boxes behandeln. Sie sehen beispielsweise dieses Programm in Python:
print("Hello World!")
Außerdem müssen Sie nicht alle Details zur Funktionsweise des print
(Zeilenformatierung, Pufferung, plattformübergreifende Unterschiede usw.). Alles, was Sie wissen müssen, ist, dass print
den übergebenen Text irgendwie druckt und Sie sich auf das konzentrieren können, was Sie in diesem Codeteil tun möchten. Dijkstra wollte, dass Sprachen diese Art der Abstraktion unterstützen.
Zu diesem Zeitpunkt wurde die Block-Syntax erfunden und Sprachen wie ALGOL sammelten ~ 5 verschiedene Arten von Steueranweisungen: Sie hatten immer noch einen sequentiellen Thread der Ausführung und gingen zu:

Und auch erfasste Bedingungen, Zyklen und Funktionsaufrufe:

Sie können diese goto
Konstrukte mit goto
implementieren, und so dachten die Leute früher über sie: als praktische Abkürzung. Aber Dijkstra wies auf den großen Unterschied zwischen goto
und den anderen Bedienern hin. Für alles andere als goto
, der Thread der Ausführung
- kommt von oben => [etwas passiert] => die Strömung kommt von unten
Wir können dies als „Black-Box-Regel“ bezeichnen. Wenn die Kontrollstruktur (Kontrolloperator) diese Form hat, können Sie in einer Situation, in der Sie sich nicht für die Details im Inneren interessieren, den Teil „etwas passiert“ ignorieren und den Block wie eine reguläre Abfolge behandeln team. Noch besser ist, dass dies für jeden Code gilt, der aus diesen Blöcken besteht. Wenn ich mir anschaue:
print("Hello World!")
Ich muss die print
und all ihre Abhängigkeiten nicht lesen, um zu verstehen, wohin der Ausführungsthread führen wird. Vielleicht gibt es innerhalb von print
eine Schleife und darin gibt es einen Zustand, in dem eine andere Funktion aufgerufen wird ... es ist alles nicht wichtig - ich weiß, dass der Thread print
, die Funktion ihren Job erledigt und der Thread schließlich zu dem Code zurückkehrt, den ich habe Lese ich
Aber wenn Sie eine Sprache mit goto
- eine Sprache, in der Funktionen und alles andere auf goto
basieren und goto
jederzeit und überall springen kann - dann sind diese Strukturen überhaupt keine Black Boxes! Wenn Sie eine Funktion mit einer Schleife haben, in der es eine Bedingung gibt und in der es goto
... dann kann dieses goto
die Ausführung überall passieren. Vielleicht kehrt die Steuerung plötzlich vollständig von einer anderen Funktion zurück, die Sie noch nicht einmal aufgerufen haben! Sie wissen nicht!
Und das bricht die Abstraktion - jede Funktion kann ein potenzielles Ziel haben, und der einzige Weg, um herauszufinden, ob dies der Fall ist, besteht darin, den gesamten Quellcode Ihres Systems im Auge zu behalten. Sobald die Sprache erreicht ist, können Sie den Ausführungsfluss nicht mehr vorhersagen. Deshalb führt goto
zu Spaghetti-Code.
Und sobald Dijkstra das Problem begriff, konnte er es lösen. Hier ist seine revolutionäre Annahme - wir sollten uns Bedingungen / Schleifen / Funktionsaufrufe nicht als Abkürzungen für goto
, sondern als grundlegende Primitive mit unseren Rechten - und wir sollten goto
vollständig aus unseren Sprachen entfernen.
Ab 2018 scheint dies ziemlich offensichtlich. Aber wie reagieren Programmierer, wenn Sie versuchen, ihre unsicheren Spielsachen aufzuheben? Im Jahr 1969 schien der Vorschlag von Dijkstra unglaublich zweifelhaft. Donald Knuth verteidigte goto
. Leute, die Experten für das Codieren mit goto
wurden, waren zu Recht empört, wieder lernen zu müssen, wie man seine Ideen in neuen, restriktiveren Begriffen ausdrückt. Und natürlich brauchte es, um völlig neue Sprachen zu schaffen.
Infolgedessen sind moderne Sprachen etwas weniger streng als der ursprüngliche Wortlaut von Dijkstra.

Links: traditionell goto
. Rechts: Gezähmt gehe zu, wie in C, C #, Golang usw. Wenn Sie die Grenzen einer Funktion nicht überschreiten, kann er immer noch auf Ihre Schuhe pinkeln, aber es ist unwahrscheinlich, dass Sie auseinander gerissen werden.
Mit ihnen können Sie die Verschachtelungsebenen von Struktursteuerungsanweisungen mithilfe von break
, continue
oder return
. Im Grunde sind sie alle auf Dijkstras Idee aufgebaut und können den sequentiellen Ablauf der Ausführung auf streng begrenzte Weise stören. Insbesondere Funktionen - ein grundlegendes Werkzeug, um einen Ausführungsstrang in eine Blackbox zu packen - sind unzerstörbar. Sie können den Befehl break
von einer Funktion zur nächsten ausführen und return
kann Sie nicht weiter als bis zur aktuellen Funktion zurückgeben. Keine Manipulation des Ausführungsthreads innerhalb der Funktion wirkt sich auf andere Funktionen aus.
Und die Sprachen, die den goto
Operator (C, C #, Golang, ...) erhalten haben, haben ihn stark eingeschränkt. Zumindest erlauben sie Ihnen nicht, von einem Körper einer Funktion zu einer anderen zu springen. Wenn Sie Assembler [2] nicht verwenden, gehört das klassische, unbegrenzte goto
der Vergangenheit an. Dijkstra gewann.
Schöne neue Welt ohne zu gehen
Mit dem Verschwinden von goto
etwas Interessantes passiert - die Sprachentwickler konnten neue Funktionen hinzufügen, die auf einem strukturierten Ablauf der Ausführung basierten.
Zum Beispiel hat Python eine coole Syntax zum automatischen Löschen von Ressourcen - einen Kontextmanager . Sie können schreiben:
und dies stellt sicher, dass die Datei zur Laufzeit some code
öffnet, danach aber sofort geschlossen wird. Die meisten modernen Sprachen haben Entsprechungen ( RAII , using
, try-with-resource, defer
, ...). Und alle gehen davon aus, dass der Kontrollfluss in Ordnung ist. Und was passiert, wenn wir mit goto
in den with
Block springen? Ist die Datei geöffnet oder nicht? Und wenn wir da rausspringen, anstatt wie gewohnt zu gehen?
Nachdem der Code innerhalb des Blocks vollständig ist, wird with
die Methode __exit__()
, with
der offene Ressourcen wie Dateien und Verbindungen geschlossen werden.
Wird die Datei geschlossen? In goto
Kontextmanager einfach nicht klar.
Das gleiche Problem bei der Fehlerbehandlung - Was soll der Code tun, wenn etwas schief geht? Häufig - Senden Sie eine Fehlerbeschreibung über den Stapel (der Aufrufe) an den aufrufenden Code und lassen Sie ihn entscheiden, was zu tun ist. Moderne Sprachen verfügen speziell dafür über Konstruktionen wie Exceptions oder andere Formen der automatischen Fehlererhebung . Diese Hilfe ist jedoch nur verfügbar, wenn die Sprache über einen Aufrufstapel und ein robustes Aufrufkonzept verfügt. Erinnern Sie sich an die Spaghetti im Flow-Beispiel im FLOW-MATIC-Beispiel und stellen Sie sich die in der Mitte ausgelöste Ausnahme vor. Wo kann es überhaupt kommen?
Nicht mehr gehen
Das traditionelle goto
das Funktionsgrenzen ignoriert, ist also nicht nur deshalb schlecht, weil es schwierig ist, es richtig zu verwenden. Wenn das nur so wäre, hätte goto
bleiben können - viele schlechte Sprachkonstrukte sind geblieben.
Aber selbst die sehr goto
Funktion in der Sprache macht alles noch komplizierter. Bibliotheken von Drittanbietern können nicht als Black Box betrachtet werden. Ohne die Quelle zu kennen, können Sie nicht herausfinden, welche Funktionen normal sind und welche den Ausführungsfluss unvorhersehbar steuern. Dies ist ein großes Hindernis für die Vorhersage des lokalen Codeverhaltens. Leistungsstarke Funktionen wie Kontext-Manager und automatische Fehler-Popups gehen ebenfalls verloren. Es ist besser, goto
insgesamt zu entfernen, und zwar zugunsten von Steueroperatoren, die die Black-Box-Regel unterstützen.
Über die Gefahren von Ausdrücken wie "Go"
Also haben wir uns die Geschichte angesehen. Aber ist es auf den go
Operator anwendbar? Alles in allem! Die Analogie ist schockierend genau.
go-Ausdrücke brechen Abstraktionen.
Weißt du noch, wie wir gesagt haben, wenn die Sprache goto
erlaubt, kann jede Funktion goto
in sich verbergen? In den meisten asynchronen Frameworks führen go
Ausdrücke zum gleichen Problem - jede Funktion kann die Aufgabe im Hintergrund ausführen (oder auch nicht). Es sieht so aus, als hätte die Funktion die Kontrolle zurückgegeben, aber funktioniert sie immer noch im Hintergrund? Und es gibt keine Möglichkeit, dies herauszufinden, ohne die Quelle der Funktion und alles, was sie aufruft, zu lesen. Und wann wird es enden? Schwer zu sagen. Wenn Sie go
und seine Analoga haben, sind Funktionen keine Black Box mehr, die den Ausführungsfluss berücksichtigen. In meinem ersten Artikel über asynchrone APIs habe ich dies als "Kausalitätsverletzung" bezeichnet und festgestellt, dass dies die Hauptursache für viele häufige, echte Probleme in Programmen ist, die asyncio
und Twisted verwenden, z.
Dies bezieht sich auf die Steuerung des Datenflusses, der in das Programm eintritt und dieses verlässt. Zum Beispiel empfängt das Programm Daten mit einer Geschwindigkeit von 3 MB / s und verlässt es mit einer Geschwindigkeit von 1 MB / s, und dementsprechend verbraucht das Programm immer mehr Speicher, siehe einen anderen Artikel des Autors
go-expressions unterbrechen die automatische Bereinigung offener Ressourcen.
Schauen wir uns noch einmal ein Beispiel with
Anweisung an:
Früher haben wir gesagt, dass wir "garantiert" sind, dass die Datei geöffnet ist, während ein some code
funktioniert, und danach geschlossen werden. Aber was ist, wenn some code
eine Hintergrundaufgabe startet? : , , with
, with
, , , . , ; , , some code
.
, , - , , .
, Python threading
— , , — , with
, , , ( ). , . , .
go- .
, , (exceptions), . " ". , . , , . , , … , . , - . ( , , " - " — ; .) Rust — , , - — . (thread) , Rust .
, , join , errbacks Twisted Promise.catch Javascript . , , . , Traceback . Promise.catch
.
, .
go
, goto
, go- — , , , . , goto
, , go
.
, , ! :
, Trio .
go
: , , , . , , :

, , , " " .
? " " ,
) , , ( ),
) , .
. , - . , .. [3]
: , , , "" , . Trio , async with
:

, as nursery
nursery
. nursery.start_soon()
, () : myfunc
anotherfunc
. . , , () , , .

, , — , , . , .
, .
:

, . Hier sind einige von ihnen:
.
go- — , , , . — , , . , , .
.
, . :
run_concurrently([myfunc, anotherfunc])
async.gather Python, Promise.all Javascript, ..
, , , . , accept
, .
accept
Trio:
async with trio.open_nursery() as nursery: while True: incoming_connection = await server_socket.accept() nursery.start_soon(connection_handler, incoming_connection)
, , run_concurrently
. , run_concurrently
— , , run_concurrently
, .
.
. , , ? : . , async with open_nursery()
nursery.start_soon()
, — [4], , , . , , .
, , " ", :
, .
, , go-, .
, .
, - . , , . :
async with my_supervisor_library.open_supervisor() as nursery_alike: nursery_alike.start_soon(...)
, , . .
Trio , asyncio
: start_soon()
, Future
( , Future
). , ( , Trio Future
!), .
, , .
, , — — .
Trio, . , , " " ( ), Cancelled
. , , — - , " ", , .. , , . , , , .
.
" ", with
. , with
, .
.
, , . .
Trio, , … - . , . , — " " — , myfunc
anotherfunc
, . , , .
, : (re-raise) , . ,
" " , , , , , .
, , . ?
— ( ) , . , , , , .
, , - ( task cancellation ). C# Golang, — .
go
goto
, with
; go
- . Zum Beispiel:
- , , , . ( : - )
- — Python ,
ctrl-C
( ). , .
, . ?
… : ! , , . , , , break
continue
.
, . — , 1970 , goto
.
. (Knuth, 1974, .275):
, goto
, , " " goto
. goto
! , , goto
, . , , . , — , — "goto" .
: . , , . , , . , , .
, Happy Eyeballs ( RFC 8305 ), TCP . , — , , . Twisted — 600 Python . 15 . , , , . , , . , . ? Die Zeit wird zeigen. .
Schlussfolgerungen
— go
, , , Futures
, Promises
,… — goto
, . goto
, -- goto
, . , , ; , . , goto
, .
, , ( CTRL+C
) , .
, , , , — , goto
. FLOW-MATIC , , - . , , Trio , , .
Kommentare
Trio .
:
Trio : https://trio.discourse.group/
Danksagung
Graydon Hoare, Quentin Pradet, and Hynek Schlawack . , , .
berez .
: FLOW-MATIC (PDF), .
Wolves in Action, Martin Pannier, CC-BY-SA 2.0 , .
, Daniel Borker, CC0 public domain dedication .
[2] WebAssembly , goto
: ,
[3] , , , , :
The "parallel composition" operator in Cooperating/Communicating Sequential Processes and Occam, the fork/join model, Erlang supervisors, Martin Sústrik's libdill , crossbeam::scope / rayon::scope Rust. golang.org/x/sync/errgroup github.com/oklog/run Golang.
, - .
[4] start_soon()
, , start_soon
, , , . , .
Über den Autor
Nathaniel J. Smith , Ph.D., UC Berkeley numpy
, Python . Nathaniel .
Fortsetzung
:
, , , Haskell , , .
( , 0xd34df00d , ) , ( Happy Eyeballs ), .
, Trio ? Haskell Golang ?
: