MicroPython: uasyncio / async

Das Asyncio-Konzept ist kooperatives Multitasking. Diese Technik ist in eingebetteten Systemen und Mikrocontrollern weit verbreitet. Es ermöglicht Multitasking auf einem vergleichsweise niedrigem Niveau. Während der Anwendungsentwickler auf einem System mit einer Universal-CPU das Multitasking dem Betriebssystem überlassen kann, muss er in Mikrocontrollern, wie zum Beispiel dem Raspberry Pi Pico, gewisse konzeptionelle Vorbereitungen treffen, damit sein Programmcode quasi parallel arbeiten kann. Und tatsächlich wird hier nichts parallel, sondern immer noch streng nacheinander ausgeführt. Allerdings kann der Programmierer innerhalb des Programmcodes Warte-Kommandos setzen, in deren Zeit ein anderer Programmteil ausgeführt wird. Bei hoher Rechenleistung fühlt sich das für den Anwender so an, als ob er ein parallel verarbeitendes System vor sich hat. Dabei ist das nur Quasiparallelität. „Quasi“ bedeutet „sozusagen“, „gewissermaßen“ oder „so gut wie“ (Quelle: Duden).

Beim kooperativen Multitasking werden zwar auch, wie beim preemptiven Multitasking, durch den Scheduler, ein Teil des Betriebssystems, Prioritäten an die einzelnen Tasks vergeben. Allerdings kann es hier vorkommen, dass der Task (die Aufgabe) mit der höchsten Priorität die Rechenzeit- und die Leistung für sich alleine beansprucht. Dadurch werden unter Umständen die anderen Tasks geblockt und müssen „warten“.

Zum besseren Verständnis muss man wissen, dass die Funktionen asynchron ausgeführt werden und dabei eine Quasiparallelität entsteht. Jede Funktion wird trotzdem nacheinander ausgeführt. Es ist wichtig, dass der Programmierer vor dem Ende einer Funktion eine Pause mit „await“ vorsieht. Nur so haben die anderen Funktionen die Möglichkeit auch Rechenzeit zu bekommen.

MicroPython-Modul „uasyncio“

Das MicroPython-Modul „uasyncio“ ermöglicht dem Programmierer mit Hilfe von kooperativem Multitasking die Quasiparallelität zu erreichen und die Rechenleistung des Mikrocontrollers auszureizen.
Das MicroPython-Modul „uasyncio“ ist sehr umfangreich und sehr komplex. Desweiteren muss man berücksichtigen, dass wenn man „uasyncio“ nutzen möchte, dass der gesamte Programmcode den Regeln dieses Konzepts folgen muss. Wenn man einen sequentiellen Programmcode nur in Teilen asynchron arbeiten lässt, dann wird dieser asynchrone Teile nicht besonders gut funktionieren bzw. nicht genug Zeit zur Verarbeitung bekommt.

Beispiel: Programmcode ohne „uasyncio“

Was macht der Programmcode? Im Programmcode gibt es zwei Funktionen. Eine Funktion gibt die Zahlen „1“ und „2“ auf der Kommandozeile aus. Dazwischen macht der Programmablauf eine Sekunde Pause. In der zweiten Funktion (Hauptprogramm) wird die erste Funktion durch die Schleife insgesamt 3 Mal ausgeführt.
Das Hauptprogramm ruft die erste Funktion auf. Davor und dahinter wird die Zeit gestoppt und ermittelt, wie lange der Programmablauf gedauert hat.

# Bibliotheken laden
import utime as time

# Funktion
def count():
    print('1')
    time.sleep(1)
    print('2')

# Hauptprogramm
def main():
    for _ in range(3): count()

# Zeitmessung starten
start = time.ticks_ms()

# Hauptprogramm ausführen
main()

# Zeitmessung stoppen und Ergebnis ausgeben
stop = time.ticks_ms()
print('Dauer der Ausführung:', time.ticks_diff(stop, start) / 1000, 'Sekunden')

Beispiel: Programmcode mit „uasyncio“

Was macht der Programmcode? Im Programmcode gibt es zwei Funktionen. Eine Funktion gibt die Zahlen „1“ und „2“ auf der Kommandozeile aus. Dazwischen macht der Programmablauf eine Sekunde Pause. In der zweiten Funktion (Hauptprogramm) wird die erste Funktion insgesamt 3 Mal asynchron ausgeführt.
Das eigentliche Hauptprogramm darunter ruft die zweite Funktion auf. Davor und dahinter wird die Zeit gestoppt und ermittelt, wie lange der Programmablauf gedauert hat.

# Bibliotheken laden
import uasyncio as asyncio

# Funktion
async def count():
    print('1')
    await asyncio.sleep(1)
    print('2')

# Hauptprogramm
async def main():
    await asyncio.gather(count(), count(), count())

# Zeitmessung starten
import utime as time
start = time.ticks_ms()

# Hauptprogramm ausführen
asyncio.run(main())

# Zeitmessung stoppen und Ergebnis ausgeben
stop = time.ticks_ms()
print('Dauer der Ausführung:', time.ticks_diff(stop, start) / 1000, 'Sekunden')

Was ist der Unterschied zwischen dem Programmcode mit und ohne „uasyncio“?

Hinweis: Grundsätzlich ist es immer schwierig Äpfel mit Birnen zu vergleichen. Das ist hier auch so. Erst einmal hat man durch die Verwendung von „asyncio“ keinen Vorteil. Im Gegenteil. Durch „asyncio“ wird der Programmcode komplizierter und unübersichtlicher. Was genau der Unterschied ist und ob man daraus einen Vorteil ziehen kann, musst Du im konkreten Anwendungsfall herausarbeiten.

Grundsätzlich sind beide Programmcodes funktional identisch. Es geht darum, die Zahlen „1“ und „2“ mit einem Zeitversatz von einer Sekunde insgesamt 3 Mal auf der Kommandozeile auszugeben.
Im Programmcode OHNE „asyncio“ erfolgt die Ausführung streng nacheinander. Entsprechend dauert die Ausführung des Programmcodes etwas über 3 Sekunden, was der Summe der Wartezeit zwischen en einzelnen Datenausgaben (print) entspricht.
Im Programmcode MIT „asyncio“ erfolgt die Ausführung verschachtelt. Das heißt, eine Pause verstreicht hier nicht ungenutzt, sondern wird für die Ausführung einer anderen Funktion verwendet, die darauf wartet ausgeführt zu werden. Zwischen der Ausgabe von „1“ und „2“ findet immer noch eine Pause von einer Sekunde statt. Allerdings wird diese nicht mehr im Programmablauf aufaddiert. Dadurch dauert der Programmablauf nur etwas über eine Sekunde.

Der Vorteil von „asyncio“ ist, dass Wartezeiten im Programmcode, die durch „time.sleep“ entstehen, von anderen Teilen des Programmcodes (Funktionen) genutzt werden können, um die Rechenleistung besser auszulasten.

Der Nachteil von „asyncio“ ist, dass die Nutzung nur Sinn macht, wenn innerhalb des Programmcodes Wartezeiten durch „time.sleep“ entstehen. Wenn kein „time.sleep“ verwendet wird, dann macht „asyncio“ keinen Sinn, es sei denn man findet einen Weg, künstliche Wartezeiten einzubauen.

Brauche ich kooperatives Multitasking?

Wenn man am Anfang eines Programmcodes steht, dann darf auch die Frage gestellt werden, ob kooperatives Multitasking sinnvoll ist. Nachträglich einen Programmcode auf „asyncio“ umzustellen, bedeutet im Regelfall fast alles neu zu schreiben.
Sollte man also grundsätzlich seinen Programmcode für kooperatives Multitasking planen?

Schwer zu sagen. Eine Lösung wie „asyncio“ macht nur dann Sinn, wenn man die in einem Programmcode eingebauten Wartenzeiten anderweitig nutzen muss oder kann, um nebenläufigen Funktionen unabhängig von der Ausführung in einer Schleife durchzuführen.
Allerdings gibt es je nach Anwendungsfall auch andere Lösungen. Beispielsweise Timer- und Interrupt-Steuerungen.

Weitere verwandte Themen:

Teilen:

Hardware-nahes Programmieren mit dem Raspberry Pi Pico und MicroPython

Elektronik-Set Pico Edition

Das Elektronik-Set Pico Edition ist ein Bauteile-Sortiment mit Anleitung zum Experimentieren und Programmieren mit MicroPython.

  • LED: Einschalten, ausschalten, blinken und Helligkeit steuern
  • Taster: Entprellen und Zustände anzeigen
  • LED mit Taster einschalten und ausschalten
  • Ampel- und Lauflicht-Steuerung
  • Elektronischer Würfel
  • Eigene Steuerungen programmieren

Elektronik-Set jetzt bestellen Online-Workshop buchen

Programmieren mit dem Raspberry Pi Pico
Online-Workshop

Hardware-nahes Programmieren mit dem Raspberry Pi Pico

Gemeinsam mit anderen und unter Anleitung experimentieren? Wir bieten unterschiedliche Online-Workshops zum Raspberry Pi Pico und MicroPython an. Einführung in die Programmierung, Sensoren programmieren und kalibrieren, sowie Internet of Things und Smart Home über WLAN und MQTT.

Online-Workshop buchen

Für Ihre Fragen zu unseren Online-Workshops mit dem Raspberry Pi Pico besuchen Sie unseren PicoTalk (Online-Meeting). (Headset empfohlen)

Zum PicoTalk

 

Elektronik-Set Pico Edition
Elektronik-Set Pico Edition

Raspberry Pi Pico: Hardware-nahes Programmieren mit MicroPython

Leichter Einstieg mit All-in-one-Set zum sofort Loslegen, um eigene Steuerungen programmieren.

Elektronik-Set jetzt bestellen

 

Elektronik-Set Pico WLAN Edition
Elektronik-Set Pico WLAN Edition

Raspberry Pi Pico W: IoT und Smart Home mit WLAN und MQTT

Betreibe Deinen Raspberry Pi Pico W als drahtloser Sensor in Deinem WLAN, versende E-Mails mit Daten und kommuniziere per MQTT im Internet of Things oder Smart Home.

Elektronik-Set jetzt bestellen

 

Elektronik-Set Sensor Edition
Elektronik-Set Sensor Edition

Erweiterung zu den Elektronik-Sets Pico Edition und Pico WLAN Edition

Elektronik-Set mit den beliebtesten Sensoren zum Messen von Temperatur, Helligkeit, Bewegung, Lautstärke und Entfernung.

Elektronik-Set jetzt bestellen