8.3. Die Ereignisschleife¶
Die Ereignisschleife ist die Engine, die Asyncio darunter ausführt. Sie führt eine Liste jedes Tasks im Programm, fordert jeden auf, bis zu seinem nächsten await zu laufen, und geht zum nächsten bereiten Task über. Wenn keine Tasks bereit sind, wartet sie — das eigentliche Warten ist es, was die CPU für die Firmware verfügbar macht, um andere Dinge auszuführen, und stromsparende Sleeps greifen lässt — bis etwas, auf das ein Task gewartet hat, verfügbar wird, und setzt diesen Task dann fort. Endlos wiederholen.
Die meisten Anwendungen interagieren nie direkt mit der Schleife. Die Schleife ist eine Folge des Aufrufs von asyncio.run(); die Anwendung schreibt Koroutinen, schedult sie als Tasks, und die Schleife erledigt den Rest.
8.3.1. Was asyncio.run() tatsächlich tut¶
Ein einzelner Aufruf:
asyncio.run(main())
ist eine Kurzform für eine längere Sequenz, die die Schleife im Auftrag der Anwendung verwaltet:
Die Ereignisschleife erstellen, falls sie noch nicht existiert.
Die übergebene Koroutine in einen Task einpacken und als Top-Level-Einstiegspunkt der Schleife schedulen.
Die Schleife ausführen — durch bereite Tasks schreiten, warten, wenn keine bereit sind, Tasks fortsetzen, wenn ihre Awaits abgeschlossen sind — bis der Top-Level-Task zurückkehrt oder eine Exception wirft.
Alle von der Anwendung erstellten Tasks abbrechen, die noch laufen.
Zurückgeben, was die Top-Level-Koroutine zurückgegeben hat (oder erneut werfen, was sie geworfen hat).
8.3.2. Eine einzige Schleife pro Programm¶
Das Asyncio von MicroPython hat eine Ereignisschleife, Punkt. Es gibt kein Erstellen einer frischen Schleife und kein Verschachteln einer Schleife in einer anderen. Der Aufruf von asyncio.run() von innerhalb einer Koroutine, die bereits auf der Schleife läuft, ist ein Fehler; die Schleife ist bereits da, und die Koroutine muss lediglich das awaiten, was sie starten wollte.
In der Praxis lautet die Regel genauso wie die Schlusszeile der vorherigen Seite: Es gibt genau einen asyncio.run()-Aufruf pro Programm, ganz oben, mit einem einzigen async def main() dahinter. Alles andere lebt innerhalb von main.
8.3.3. Direkter Zugriff auf die Schleife¶
Für die seltenen Fälle, in denen eine Anwendung die Schleife selbst berühren muss — meist Diagnose und Exception-Handler — gibt asyncio.get_event_loop() das Loop-Objekt zurück. Von dort aus kann die Anwendung einen benutzerdefinierten Exception-Handler installieren, inspizieren, was die Schleife tut, oder (sehr gelegentlich) create_task() direkt anstelle von asyncio.create_task() aufrufen (es ist dieselbe Operation).
Der vollständige Satz an Methoden, den Loop bereitstellt — run_forever(), stop(), set_exception_handler() und die übrigen — wird auf der Seite Schleifensteuerung später in diesem Abschnitt behandelt. Bis dahin ist asyncio.run(main()) alles, was eine Anwendung benötigt.