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?
Python und asyncio
![]() Anmeldungsdatum: Beiträge: 742 |
|
Projektleitung
Anmeldungsdatum: Beiträge: 12527 |
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 Hier steht eine Erklärung - allerdings auf Englisch. Das Beispiel mit Judith erklärt es eigentlich recht gut. |
(Themenstarter)
![]() Anmeldungsdatum: 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? |
![]() Anmeldungsdatum: 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. |
![]() Anmeldungsdatum: Beiträge: 17432 |
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. |
![]() Anmeldungsdatum: 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... |
Ehemaliger
![]() Anmeldungsdatum: Beiträge: 28316 |
Hallo,
Das ist im Kern dein Denkfehler, weil:
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 |
(Themenstarter)
![]() Anmeldungsdatum: Beiträge: 742 |
Ok dann frag ich mal andersrum: aus OS-Sicht gibt es einige böse Dinge, die man vermeiden muss:
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? |
Ehemaliger
![]() Anmeldungsdatum: 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).
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 |
(Themenstarter)
![]() Anmeldungsdatum: Beiträge: 742 |
Dann stellt sich immer noch die Frage, welches Problem asyncio bei home assistant löst? Sind denn alle Webserver asynchron programmiert? |
Ehemaliger
![]() Anmeldungsdatum: Beiträge: 28316 |
Hallo,
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?
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 |
Projektleitung
Anmeldungsdatum: Beiträge: 12527 |
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. |
(Themenstarter)
![]() Anmeldungsdatum: Beiträge: 742 |
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. |
Ehemaliger
![]() Anmeldungsdatum: 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.
Was ich sagen wollte: unter CPython laufen aus Python heraus gestartet Threads, also via Gruß, noisefloor |