staging.inyokaproject.org

Python und asyncio

Status: Ungelöst | Ubuntu-Version: Ubuntu 22.04 (Jammy Jellyfish)
Antworten |

jms3000

Avatar von jms3000

Anmeldungsdatum:
29. Januar 2015

Beiträge: 742

Ich lese mich gerade ein bisschen ein in die asyncio-Programmierung mit Python. Was ich nicht verstehe: ich dachte eigentlich dass Linux "preemptive multitasking" macht. Das bedeutet doch eigentlich: macht ein Programm eine Aktion, die blockt, wie z.B. I/O, Sleep, Wait, Socket-IO, dann bekommt es vom Scheduler die CPU weggenommen und das nächste Programm bekommt die CPU. Wenn also ein Python-Programm time.sleep() macht, dann wird doch wohl kaum die CPU wirklich damit beschäftigt sein, die Sekunden für den Sleep zu zählen. Oder wenn auf die Antwort eines http-Requests gewartet wird, wird doch die CPU keinen idle-Loop machen und permanent das Ergebnis pollen. Welchen Vorteil hat dann die asyncio-Technik, wenn das Betriebssystem das schon von sich aus macht?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

jms3000 schrieb:

Welchen Vorteil hat dann die asyncio-Technik, wenn das Betriebssystem das schon von sich aus macht?

Das normale (und für die Programmiererin am leichtesten zu verstehende) Verfahren ist ja blockierender IO. Wenn man das nutzt, ist der Thread, der z.B. den sleep aufruft oder von einem Dateideskriptor liest, geblockt. Das ist erst einmal nicht schlimm, aber der Overhead (im Sinne von Speicherverbrauch) eines Threads ist schon bedeutsam (jeder Thread braucht einen Callstack), wenn man viele IO-Operationen gleichzeitig hat (wie z.B. bei einem Webserver mit viel Traffic).

Ich kenne jetzt asyncio nur vom kurzen Lesen der Doku (s.u.). Zentral sind Coroutinen, die selbständig die Kontrolle abgeben können, so dass etwas anderes ausgeführt werden kann. Das spart jede Menge Threads und erlaubt damit, mit weniger Resourcen mehr zu schaffen.

Hier steht eine Erklärung - allerdings auf Englisch. Das Beispiel mit Judith erklärt es eigentlich recht gut.

jms3000

(Themenstarter)
Avatar von jms3000

Anmeldungsdatum:
29. Januar 2015

Beiträge: 742

Meinst du wirklich der Overhead von Threads ist so groß? Ich beschäftige mit viel mit home assistant und diese async-Programmierung ist sehr verwirrend. Auf der anderen Seite scheint es tatsächlich keine Threads zu geben, es ist nur ein Programmfaden. Aber irgendwo muss der Zustand eines unterbrochenen Aufrufs doch auch gespeichert werden?

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2119

Es geht darum, dass der Aufruf in Python blockiert. Was das Betriebssystem intern damit macht, ist an der Stelle nicht relevant. Der Programmfluss bleibt halt stehen. Mit asynchroner Programmierung, Multiprocessing, Threads und ähnlichen Kniffen umgeht man das Problem. Zu beachten ist, dass Python-Threads aus technischen Gründen nicht in der Lage sind, mehrere Kerne zu nutzen. Insofern wird das nur für I/O-lastige Aufgaben (z.B. im Netzwerk) empfohlen. Aufwändige Berechnungen werden daher mit Python-Threads nicht schneller, da effektiv eben nur ein Kern genutzt wird. Eher sogar etwas langsamer aufgrund des Verwaltungsaufwands für die Threads.

Nebenläufige Programmierung - egal auf welcher Grundlage - ist immer etwas komplizierter. Je nach Aufgabengebiet gibt es diverse externe Bibliotheken, die du für Python nutzen kannst. Mit deren API ist es meist nicht mehr ganz so umständlich in der Umsetzung.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17432

jms3000 schrieb:

Welchen Vorteil hat dann die asyncio-Technik, wenn das Betriebssystem das schon von sich aus macht?

Richtig ist, dass andere Programme weiterlaufen können, während ein Programm x auf die Antwort zu einem Webrequest wartet oder ein Sleep durchführt.

Aber das Programm x selbst ist so lange blockiert, außer eben man trifft Vorkehrungen, um Teile des Programms, die parallel laufen können, auszuführen. Diese asyncio-Geschichten sind wahrscheinlich genau so eine Vorkehrung. Trivial ist das ja oft nicht, weil das Programm ja mit Ergebnissen der Abfrage oft etwas anfangen will.

Vielleicht muss das Programm ja 10 Adressen abfragen und kann die Abfrage 2..10 schon starten, während Thread 1 schon wartet.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2119

Hier übrigens ein Beispiel für asynchrone HTTP-Abfragen:

https://www.twilio.com/blog/asynchronous-http-requests-in-python-with-aiohttp

Meistens reicht sowas schon aus, um gängige Probleme zu lösen. Für einen tieferen Einstieg ist natürlich mehr Einarbeitung notwendig. Da kenne ich mich dann aber auch nicht mehr im Detail aus...

noisefloor Team-Icon

Ehemaliger
Avatar von noisefloor

Anmeldungsdatum:
6. Juni 2006

Beiträge: 28316

Hallo,

Welchen Vorteil hat dann die asyncio-Technik, wenn das Betriebssystem das schon von sich aus macht?

Das ist im Kern dein Denkfehler, weil:

Was das Betriebssystem intern damit macht, ist an der Stelle nicht relevant.

Das Betriebssystem verwaltet _alle_ laufenden Programme. Aber nicht desto trotz kann jedes Programm X Prozess oder Threads haben. Bei Go kannst du z.B. Goroutines nutzen, um X Sachen quasi gleichzeitig zu machen. Die Standardimplementierung von Python, CPython, hat den GIL 🇬🇧, d.h. CPython kann per se nur einen Thread gleichzeitig handhaben, völlig unabhängig vom darunter liegenden Betriebssystem. Ein IMHO sehr interessanter (wenn auch älteres) Video dazu gibt es von Dave Beazly 🇬🇧. Es gibt übrigens auch andere Python-Implementierungen, die keinen GIL haben.

Bei asyncio läuft genau ein Thread, der Event Loop, der aber X Aufgaben verwaltet, die Tasks bzw. Coroutines. Macht wie schon gesagt wurde nur bei I/O lastigen Sachen (wie Netzwerksachen) Sinn, nicht bei CPU-lastigen Sachen.

Nebenläufige Programmierung ist generell nicht einfach zu verstehen, weil man sich gedanklich vom linearen Programmablauf verabschieden muss. Bei Event-Loops ist es noch ein Stufe schwieriger, weil am Ende der Event-Loop entscheidet, wann was wie warum dran ist.

Im Kern ist asyncio ja auch entstanden, um im ersten Linie in der Implementierung von Python Netzwerk I/O effizient abbilden zu können, ohne auf externe Bibliotheken wie Twisted nutzen zu müssen.

Wenn du dich mit asyncio beschäftigen willst, dann lohnt sich IMHO auch ein Blick auf Trio 🇬🇧. Sehr gute Doku und IMHO eine freundlichere API als asyncio in Python.

Gruß, noisefloor

Gruß, noisefloor

jms3000

(Themenstarter)
Avatar von jms3000

Anmeldungsdatum:
29. Januar 2015

Beiträge: 742

Ok dann frag ich mal andersrum: aus OS-Sicht gibt es einige böse Dinge, die man vermeiden muss:

  • die CPU sinnlos zu verbraten, z.B. in Loops die verzögern

  • zu hoher Speicheroverhead durch zu viel Verwaltungsaufwand

  • zu hoher CPU-Overhead durch überflüssige Kontextwechsel

Overhead durch zu viel I/O lasse ich mal weg, denn wenn I/O gemacht werden muss kommt man nicht drum herum. Welcher dieser 3 Punkte wird durch asynchrones Programmieren verhindert? Oder falls es keiner dieser 3 ist: welches Problem wird durch asynchrones Programmieren gelöst?

noisefloor Team-Icon

Ehemaliger
Avatar von noisefloor

Anmeldungsdatum:
6. Juni 2006

Beiträge: 28316

Hallo,

die Frage macht so keinen Sinn. Entweder hast du ein Problem, was (nur) durch nebenläufige Programmierung gelöst werden kann - dann nutzt du nebenläufige Programmierung. Und wenn nicht, dann nicht. Und das Betriebssystem hat damit immer noch nichts zu tun. Wenn überhaupt die Hardware.

CPU Zeit und Speicher kannst du auch auch mit einem linear laufenden Programm verbraten, dafür ist keine nebenläufige Programmierung notwendig.

Bei Python bzw. CPython ist je nach Problemstellung eher die Frage, ob du Prozess-basierte Nebenläufigkeit nutzt (Multiprocessing, mehrere Prozesse können auf Mehrkern CPU auch wirklich parallel laufen) oder kooperatives Multitasking (asyncio, Threads).

welches Problem wird durch asynchrones Programmieren gelöst?

asyncio: Viele Netzwerkverbindungen in einem Thread / Event-Loop verwalten.

Man kann das auch anders sagen: wenn man nicht weiß, welches Problem asyncio für einen selber lösen könnte, dann braucht man auch kein asyncio.

Gruß, noisefloor

jms3000

(Themenstarter)
Avatar von jms3000

Anmeldungsdatum:
29. Januar 2015

Beiträge: 742

noisefloor schrieb:

wenn man nicht weiß, welches Problem asyncio für einen selber lösen könnte, dann braucht man auch kein asyncio.

Dann stellt sich immer noch die Frage, welches Problem asyncio bei home assistant löst?

Sind denn alle Webserver asynchron programmiert?

noisefloor Team-Icon

Ehemaliger
Avatar von noisefloor

Anmeldungsdatum:
6. Juni 2006

Beiträge: 28316

Hallo,

Dann stellt sich immer noch die Frage, welches Problem asyncio bei home assistant löst?

Welcher Home Assistant genau? Wenn der Code Open Source ist kannst du ja den Quellcode durchlesen. Oder die Entwickler fragen.

Was ich mir verstellen könnte: beim Home Assistant geht es im Kerne wahrscheinlich um die Kommunikation mit X Geräten, unterm Strich also in irgendeiner Form Netzwerk-IO.

Falls du selber Code für ein Gerät schreiben oder was ist den Hintergrund der Frage?

Sind denn alle Webserver asynchron programmiert?

Nein. Die gibt es in allen Formen und Farben, von single-thread kann Requests nur sequentiell abarbeiten über nebenläufig via Thread- oder Prozessworker bis hin zu asynchron in einem Event-Loop.

Gruß, noisefloor

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

noisefloor schrieb:

Bei Python bzw. CPython ist je nach Problemstellung eher die Frage, ob du Prozess-basierte Nebenläufigkeit nutzt (Multiprocessing, mehrere Prozesse können auf Mehrkern CPU auch wirklich parallel laufen) oder kooperatives Multitasking (asyncio, Threads).

Da muss man etwas genauer sein: das Programmiermodell von Threads ist nicht kooperativ, weil der Programmierer die Maßnahmen zur Abgabe der Kontrolle - anders als bei asyncio - nicht sieht bzw. nutzen muss. Man könnte höchstens Green Threads als kooperativ bezeichnen, aber das bezieht sich dann auf deren Implementierung.

jms3000

(Themenstarter)
Avatar von jms3000

Anmeldungsdatum:
29. Januar 2015

Beiträge: 742

rklm schrieb:

noisefloor schrieb:

Da muss man etwas genauer sein: das Programmiermodell von Threads ist nicht kooperativ, weil der Programmierer die Maßnahmen zur Abgabe der Kontrolle - anders als bei asyncio - nicht sieht bzw. nutzen muss. Man könnte höchstens Green Threads als kooperativ bezeichnen, aber das bezieht sich dann auf deren Implementierung.

Ich komme eher vom Mainframe und da ist es so dass einem Programm bei jeder Aktion die nicht im Programm geschieht erst einmal die Kontrolle entzogen wird. Also bei jeder I/O usw. wird die CPU vom Scheduler neu zugeteilt. Kooperation wäre also nur dann nötig, wenn ein Programm numerische Berechnungen macht und dazu minutenlang reinen Programmcode ausführt. In dem Fall bekommt ein Programm die CPU gewaltsam weggenommen, je nach Anwendungspriorität.

noisefloor Team-Icon

Ehemaliger
Avatar von noisefloor

Anmeldungsdatum:
6. Juni 2006

Beiträge: 28316

Hallo,

@jms3000: nochmal im Klartext - solange du nicht vergisst, was auf Hardware- und das Betriebssystemeben passiert, wenn du dich mit asyncio, Threads und Multiprocessing unter Python beschäftigst, dann stehst du dir krass selber im Weg. Relevant ist in 1. Instanz alleine, wie der Python-Interpreter das umsetzt.

rklm schrieb:

Da muss man etwas genauer sein: das Programmiermodell von Threads ist nicht kooperativ, ...

Was ich sagen wollte: unter CPython laufen aus Python heraus gestartet Threads, also via threading oder concurrent.futures, nie parallel, eben weil es den GIL gibt und deswegen nur ein Thread gleichzeitig von Python-Intetrprter gehandhabt wird. Es gibt andere Python-Implementierung und auch andere Programmiersprache, wo Thread parallel laufen können.

Gruß, noisefloor

Antworten |