2.32. Coroutinen¶
Eine Coroutine ist eine Funktion, die mitten in ihrer Ausführung pausieren und später dort fortfahren kann, wo sie aufgehört hat – mit allen lokalen Variablen unverändert. Sie spiegelt das Generator-Muster wider – ein Funktionsrumpf, der angehalten und fortgesetzt werden kann – mit einer Änderung dabei, wer jede Fortsetzung antreibt.
Pythons Syntax zum Schreiben einer Coroutine ist das Schlüsselwortpaar async / await. async kennzeichnet die Funktion als Coroutine; await markiert die Stellen darin, an denen ein Pausieren erlaubt ist.
2.32.1. Eine Coroutine definieren¶
Eine mit async def definierte Funktion ist eine Coroutine-Funktion. Ihr Aufruf führt den Rumpf nicht aus; er liefert ein Coroutine-Objekt zurück, das noch nicht gestartet wurde:
async def greet(name):
print("hello,", name)
coro = greet("Alice") # body NOT run yet
Das Coroutine-Objekt ist ganz am Anfang der Funktion pausiert. Etwas muss es antreiben, damit der Rumpf läuft – dieses Etwas ist eine Ereignisschleife, eine Laufzeitkomponente. MicroPython liefert eine im asyncio-Modul mit. Betrachten Sie die Coroutine vorerst als „bereit zum Ausführen, wartend auf einen Antrieb“.
2.32.2. Innerhalb einer Coroutine pausieren¶
Innerhalb einer Coroutine setzt ein await-Ausdruck die Ausführung aus, bis der erwartete Wert bereit ist:
async def fetch_and_log():
data = await read_sensor()
print("got:", data)
Wenn der Rumpf await read_sensor() erreicht, gibt die Coroutine die Kontrolle an das zurück, was sie ausführt. Wenn read_sensor() fertig ist, setzt der Antrieb die Coroutine in der nächsten Zeile fort, wobei das Ergebnis an data gebunden wird.
await ist nur innerhalb einer Coroutine gültig. Es in einer normalen Funktion zu verwenden, ist ein Syntaxfehler.
2.32.3. Beziehung zu Generatoren¶
Coroutinen und Generatoren teilen denselben zugrunde liegenden Mechanismus. Der Unterschied liegt darin, wer jede Fortsetzung anstößt:
Ein Generator liefert Werte; der Verbraucher holt den nächsten mit
next()oder durch Iteration.Eine Coroutine liefert Kontrolle; eine Ereignisschleife plant die Fortsetzung, wenn die erwartete Operation bereit ist.
Wenn der yield-Handschlag des Generators einleuchtet, ist der Handschlag der Coroutine dieselbe Idee – nur angetrieben von einer Ereignisschleife statt von einer for-Schleife.
Eine Ereignisschleife ist ein kleiner Dispatcher, der eine Liste von Coroutinen führt, die auf etwas warten (einen Timer, ein Netzwerkereignis, das Beenden einer anderen Coroutine). In jeder Iteration wählt sie eine Coroutine aus, deren Wartebedingung erfüllt wurde, setzt sie bis zum nächsten await fort, vermerkt dann, worauf diese Coroutine nun wartet, und geht zur nächsten bereiten über. Das Ergebnis sind viele Aufgaben, die nebenläufig in einem einzigen Thread Fortschritte machen – jede Coroutine gibt an ihren await-Punkten freiwillig die Kontrolle ab, und die Schleife füllt diese Momente mit allen anderen Coroutinen, die zum Fortschreiten bereit sind.
Unter der Haube nutzen await und yield dasselbe Python-Laufzeitmerkmal zum Anhalten und Fortsetzen einer Funktion. Die Schlüsselwörter unterscheiden sich, weil sich die zugehörige Konvention unterscheidet: yield gibt einen Wert an einen Verbraucher zurück, der mit next() holt; await gibt die Kontrolle an eine Ereignisschleife ab, die die Fortsetzung plant, wenn die erwartete Operation bereit ist. async / await ist im Wesentlichen neuere Syntax für das Coroutine-Muster – ältere Bibliotheken bauten Coroutinen direkt auf der Generator-Maschinerie auf und verwendeten yield from (eingeführt in Iteratoren und Generatoren), um das Anhalten zwischen Coroutinen zu delegieren.
2.32.4. Coroutinen brauchen einen Antrieb¶
Eine Coroutine ist ohne eine Laufzeit, die sie antreibt, untätig. Eine zu definieren ist in Ordnung; eine auszuführen erfordert eine Ereignisschleife. MicroPythons asyncio-Modul stellt diese Ereignisschleife bereit. Der Abschnitt Asyncio behandelt, wie man die Schleife startet, Coroutinen darauf einplant, Zustand zwischen ihnen mit Locks und Events teilt, Abbruch und Zeitüberschreitungen handhabt und eine echte Anwendung rund um die hier eingeführten Schlüsselwörter async / await gestaltet.