Zum Inhalt springen
L

Wikipedia · einfach zusammengefasst · Stand

Koroutine

In der Informatik sind Koroutinen (auch Coroutinen) eine Verallgemeinerung des Konzepts einer Prozedur oder Funktion. Der prinzipielle Unterschied zwischen …

Inhalt5 Abschnitte
  1. 1. Grundidee
  2. 2. Simulation und Implementierung
  3. 3. Python
  4. 4. C, C++ und C#
  5. 5. Weitere Sprachen

Grundidee

Koroutinen, auch Coroutinen, sind in der Informatik eine Verallgemeinerung von Prozeduren oder Funktionen. Der zentrale Unterschied ist: Eine normale Prozedur läuft nach dem Aufruf bis zur Rückgabe durch, während eine Koroutine ihren Ablauf unterbrechen und später an derselben Stelle wieder aufnehmen kann. Dabei behält sie ihren Status, also zum Beispiel lokale Werte oder die aktuelle Position im Ablauf.

Koroutinen haben dadurch einen eigenen Lebenszyklus. Sie können gestartet, angehalten und fortgesetzt werden. Das ermöglicht einen hohen Grad an Nebenläufigkeit und kooperativem Multitasking: Mehrere Abläufe wechseln sich ab, indem sie freiwillig die Kontrolle abgeben. Eine Koroutine sollte dabei möglichst nicht von äußerem Zustand abhängen; ihre Funktionsparameter sollen genügen, damit die Berechnung im Funktionskörper korrekt ablaufen kann.

Frühe Programmiersprachen mit Unterstützung für Koroutinen waren Simula und Modula-2. Auch moderne Sprachen wie Python kennen ähnliche Konzepte. In Sprachen wie C oder Java ist eine direkte Implementierung schwieriger. Der Begriff stammt von Melvin Conway, der ihn 1963 in einer Veröffentlichung über Compilerbau verwendete. Donald Knuth bezeichnete Prozeduren als Spezialfall von Koroutinen.

Simulation und Implementierung

Koroutinen können auch in Sprachen simuliert werden, die sie nicht direkt unterstützen. Ein sprachunabhängiger Weg ist die Verwendung von Threads. Dabei laufen mehrere Ausführungsstränge abwechselnd und warten nach Abgabe der Kontrolle, bis sie diese wieder erhalten.

Eine andere Möglichkeit ist das sogenannte Aufrollen von Koroutinen. Dabei muss die Koroutine ihren Status selbst speichern, zum Beispiel in einer globalen Variablen. Bei jedem Aufruf springt das Programm dann an die passende Stelle im Code. Das ist jedoch nicht in jeder Sprache einfach, weil viele Programmiersprachen keinen Sprung mitten in einen Block, etwa in eine Schleife, erlauben. Solche Kontrollstrukturen müssen dann ebenfalls durch Simulationen ersetzt werden.

Python

Python unterstützt Koroutinen über die asyncio API. Seit Python 3.5 gibt es dafür die Schlüsselwörter async und await. Eine Koroutine wird mit async def definiert. await wartet auf die Fertigstellung einer Koroutine und gibt während der Wartezeit die Ausführungskontrolle zurück. Dadurch können Koroutinen nebenläufig ausgeführt werden und kooperatives Multitasking ermöglichen.

Das Artikelbeispiel startet drei Aufgaben mit asyncio.create_task und wartet mit asyncio.gather auf alle. Weil await asyncio.sleep(1) den Thread nicht blockiert, dauern die drei Wartevorgänge zusammen nur etwas über eine Sekunde.

Eine weitere Möglichkeit in Python sind Generatoren. Mit yield kann der Ablauf einer Funktion vorübergehend unterbrochen werden. Beim Aufruf einer Generator-Funktion entsteht intern ein Objekt, das den Status hält. Beim Abgeben der Kontrolle wird das vollständige Stackframe zwischengespeichert und bei Wiederaufnahme wiederhergestellt. Das Beispiel zur Fibonaccifolge erzeugt nacheinander Werte bis zu einem angegebenen limit.

In Python 2.5 wurde die Syntax von yield erweitert, um kooperatives Multitasking zu ermöglichen. In Python 3 folgten Erweiterungen zur Nutzung von Generatoren als Koroutinen, unter anderem durch async und await. In Version 3.11 wurde diese Funktionalität wieder entfernt. Generatoren mit yield bleiben Teil der Sprache; async und await sind für die asyncio API vorgesehen.

C, C++ und C#

C unterstützt weder Koroutinen noch vergleichbare Konstrukte direkt. Es gibt aber Möglichkeiten, sie zu simulieren. Eine bekannte Technik geht auf Simon Tatham zurück und ist unter anderem wegen ihrer Verwendung im SSH-Client PuTTY bekannt. Das Artikelbeispiel berechnet die Fibonaccifolge, indem der Status in Variablen first und second gespeichert wird. Da dieser Status global vorgehalten wird, kann von jeder Koroutine immer nur eine Instanz ablaufen. Durch Thread-local Storage kann der Zustand pro Thread getrennt gespeichert werden, sodass bei mehreren Threads mehrere Instanzen parallel möglich sind.

In C++ ermöglicht Boost.Coroutine, ein offizieller Bestandteil der Boost-Libraries, die Verwendung von Koroutinen. Anders als in Python ist jede Koroutine dort mit einem Stack verbunden. Dadurch sind Umschaltungen und Sprünge auch aus Unterfunktionen heraus möglich. Über Boost.Context werden ARM, MIPS, PowerPC, SPARC und X86 auf POSIX, Mac OS X und Windows unterstützt. Seit C++20 sind Koroutinen nativ in die Sprache integriert.

C# zeigt im Artikel eine koroutinenähnliche Verwendung über IEnumerable<int> und yield return. Eine Funktion fibonacci gibt dabei nacheinander Werte der Fibonaccifolge zurück, ohne alle Werte vorher in einer Liste speichern zu müssen.

Weitere Sprachen

D unterstützt Koroutinen in objektorientierter Umgebung unter dem Namen Fibers. Die Umschaltung erfolgt intern durch eine einfache Vertauschung des Stackpointers. Laut Artikel ist dies bisher nur für x86 unter Windows und Posix sowie für PowerPC verfügbar. Besonders deutlich werden Vorteile bei rekursiven Funktionen, zum Beispiel bei der Traversierung von Binärbäumen.

Tcl unterstützt Koroutinen bereits im Sprachkern und grundsätzlich plattformunabhängig. Das Beispiel erzeugt mit coroutine eine Fibonaccifunktion, die bei jedem Aufruf den nächsten Wert liefert.

PicoLisp unterstützt Koroutinen als eingebautes Sprachelement, allerdings nur in der 64-bit version. Das Beispiel definiert ebenfalls eine Fibonaccifunktion, die mit yield Werte liefert.

Kotlin nutzt im Beispiel die sequence-Funktion, um eine Koroutine zu erzeugen. Mit yield wird der Sequenz ein Wert bereitgestellt und die Koroutine unterbrochen. Durch take(7) wird sie insgesamt siebenmal unterbrochen und sechsmal fortgesetzt; ausgegeben wird [1, 1, 2, 3, 5, 8, 13].

Weiterlesen

Informatik Als einfache Rechengeräte leisteten Abakus und später der Rechenschieber unschätzbare Dienste. 1641 konstruierte Blaise Pascal eine mechanische … Funktion (Programmierung) Eine Funktion (englisch function) ist in der Informatik und in verschiedenen höheren Programmiersprachen die Bezeichnung eines Programmkonstrukts, … Programmiersprache Bei deklarativen Programmiersprachen ist der Ausführungsalgorithmus schon vorab festgelegt und wird nicht im Quelltext ausformuliert/beschrieben, sondern es … Python (Programmiersprache) Python ([ˈpʰaɪθn̩], [ ˈpʰaɪθɑn], auf Deutsch auch [ ˈpʰyːtɔn]) ist eine universell nutzbare, üblicherweise interpretierte, höhere Programmiersprache. C (Programmiersprache) C ist eine imperative und prozedurale Programmiersprache, die der Informatiker Dennis Ritchie in den frühen 1970er Jahren an den Bell Laboratories entwickelte. Java (Programmiersprache) Java ist eine objektorientierte Programmiersprache und eine eingetragene Marke des Unternehmens Sun Microsystems, welches 2010 von Oracle übernommen wurde. Thread (Informatik) Kritischer Abschnitt · Nebenläufigkeit · Parallele Programmierung · Prozess · Threadsicherheit. Literatur. Bearbeiten. Peter Ziesche: Nebenläufige & verteilte … Schleife (Programmierung) Eine Schleife (auch „Wiederholung“ oder englisch loop) ist eine Kontrollstruktur in Programmiersprachen. Sie wiederholt einen Anweisungs-Block – den … Programmierschnittstelle Im Gegensatz zu einer Binärschnittstelle (ABI) definiert eine Programmierschnittstelle nur die Programmanbindung auf Quelltext-Ebene. Die Bereitstellung einer … Secure Shell Secure Shell oder SSH bezeichnet ein kryptographisches Netzwerkprotokoll für den sicheren Betrieb von Netzwerkdiensten über ungesicherte Netzwerke. Binärbaum Binärbäume sind in der Informatik die am häufigsten verwendete Unterart der Bäume. Im Gegensatz zu anderen Arten von Bäumen können die Knoten eines …