select.epoll() vs async framework (PostgreSQL)

Hallo,
bisher nutze ich psycopg (PostgreSQL) zusammen mit Linux select.epoll()
und das klappt eigentlich ganz gut. Bisher nutze ich kein async IO framework.
Jetzt soll neben der Datenbank Connection auch noch N URLs geholt werden.
Aktuell ist N < 1000. Also weniger als 1000 URLs die gleichzeitig geholt werden.
Jetzt bin ich unsicher, ob ich:
Variante1: die N URLs auch per select.epoll() hole.
Variante2: auf ein async IO Framework umsteige.
Hat hier jemand Erfahrung und kann ggf etwas aus dem Nähkästchen plaudern?
Gruß,
Thomas

Thomas Güttler Lists schrieb am 03.01.2018 um 22:07:
bisher nutze ich psycopg (PostgreSQL) zusammen mit Linux select.epoll()
und das klappt eigentlich ganz gut. Bisher nutze ich kein async IO framework.
Jetzt soll neben der Datenbank Connection auch noch N URLs geholt werden.
Aktuell ist N < 1000. Also weniger als 1000 URLs die gleichzeitig geholt werden.
Jetzt bin ich unsicher, ob ich:
Variante1: die N URLs auch per select.epoll() hole.
Variante2: auf ein async IO Framework umsteige.
Hat hier jemand Erfahrung und kann ggf etwas aus dem Nähkästchen plaudern?
Deine Frage ist doch eigentlich, ob du an dein selbst erfundenes Sperrholzrad noch Spikes anmontieren sollst, oder statt dessen das voll erweiterbare, luftgepolsterte Alufelgenrad einbauen sollst, das alle anderen auch nehmen.
Ich lasse die Antwort mal offen.
Stefan

Was ist aus deiner Sicht das, was alle nehmen?
Am 12.01.2018 um 11:23 schrieb Stefan Behnel:
Thomas Güttler Lists schrieb am 03.01.2018 um 22:07:
bisher nutze ich psycopg (PostgreSQL) zusammen mit Linux select.epoll()
und das klappt eigentlich ganz gut. Bisher nutze ich kein async IO framework.
Jetzt soll neben der Datenbank Connection auch noch N URLs geholt werden.
Aktuell ist N < 1000. Also weniger als 1000 URLs die gleichzeitig geholt werden.
Jetzt bin ich unsicher, ob ich:
Variante1: die N URLs auch per select.epoll() hole.
Variante2: auf ein async IO Framework umsteige.
Hat hier jemand Erfahrung und kann ggf etwas aus dem Nähkästchen plaudern?
Deine Frage ist doch eigentlich, ob du an dein selbst erfundenes Sperrholzrad noch Spikes anmontieren sollst, oder statt dessen das voll erweiterbare, luftgepolsterte Alufelgenrad einbauen sollst, das alle anderen auch nehmen.
Ich lasse die Antwort mal offen.
Stefan _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de

On 15. Jan 2018, at 16:46, Thomas Güttler guettliml@thomas-guettler.de wrote:
Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Diez
Am 12.01.2018 um 11:23 schrieb Stefan Behnel:
Thomas Güttler Lists schrieb am 03.01.2018 um 22:07:
bisher nutze ich psycopg (PostgreSQL) zusammen mit Linux select.epoll()
und das klappt eigentlich ganz gut. Bisher nutze ich kein async IO framework.
Jetzt soll neben der Datenbank Connection auch noch N URLs geholt werden.
Aktuell ist N < 1000. Also weniger als 1000 URLs die gleichzeitig geholt werden.
Jetzt bin ich unsicher, ob ich:
Variante1: die N URLs auch per select.epoll() hole.
Variante2: auf ein async IO Framework umsteige.
Hat hier jemand Erfahrung und kann ggf etwas aus dem Nähkästchen plaudern?
Deine Frage ist doch eigentlich, ob du an dein selbst erfundenes Sperrholzrad noch Spikes anmontieren sollst, oder statt dessen das voll erweiterbare, luftgepolsterte Alufelgenrad einbauen sollst, das alle anderen auch nehmen. Ich lasse die Antwort mal offen. Stefan _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de
-- Thomas Guettler http://www.thomas-guettler.de/ I am looking for feedback: https://github.com/guettli/programming-guidelines _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de

On 15.01.2018 17:07, Diez B. Roggisch wrote:
On 15. Jan 2018, at 16:46, Thomas Güttler guettliml@thomas-guettler.de wrote:
Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Diez
Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Sockets sind meiner Meinung nach ein viel besseres Abstraktionsniveau als meinen Quellcode überall mit diesem dämlichen async und await zu spicken.
Das einzige, was hier fehlt, wäre ein spezieller Typ von File-Deskriptor, der entsprechend in der Lage ist HTTP zu sprechen.
*Kooperatives Multitasking ist einfach nur Schrott.* Ich habe mich neulich mit einem Mitt-Fünziger unterhalten, der asyncio in seinem neusten Projekt einsetzt, und habe ihn gefragt, wie er es findet und ob es sich irgendwie zu kooperativen Multitasking aus Mainframe-Zeiten (was bekanntlich aus der Mode gekommen ist) unterscheidet.
Er sagte folgendes:
naaa it’s the same thing .. like a loop u know .. marketing .. blah blah .. some technology went out from window .. and back from doors .. like
something new
*we call that progress* .. but is something like reinvent the wheel
for sell something ;)
ppl need “new” words, “new” languages .. to feel young :-)
Selbst ich erinnere mich noch daran, wie ich im Kurs Betriebssysteme dieses Thema einfach nur zum Reihern fande. Um Scheduling soll sich gefälligst das Betriebssystem kümmern. Das können die Leute aus der Ecke eh viel besser als ich und ich befürchte die meisten, die asyncio einsetzen überschätzen ihre Fähigkeiten diesbzgl. total.
Sven
PS: der liebe Mitt-Fünfziger meinte am Ende noch er müsste jetzt nochmal schauen, warum sich in seiner Anwendung irgendwelche laufenden Coroutinen nicht beenden. Eigentlich sollte doch das die Programmiersprache für ihn tun... naja, ein selbst-gehäkeltes Betriebssystem ist halt doch nicht so einfach, wie manche hier auf den Python-Mailinglisten behaupten.
PPS: Übersetzung von oben
naaa es ist dasselbe .. eine Art Schleife du weißt schon .. Marketing .. bla bla .. manche Technologie wurde aus dem Fenster geworfen .. und kam
postwendend durch die Tür wieder herein .. als etwas neues
*wir nennen das dann Fortschritt* .. aber das ist wie Rad neu zu
erfinden um etwas zu verkaufen ;)
Leute brauchen "neue" Wörter, "neue" Sprachen .. um sich jung zu
fühlen :-)

On 2018-01-15 20:55, Sven R. Kunze wrote:
On 15.01.2018 17:07, Diez B. Roggisch wrote:
On 15. Jan 2018, at 16:46, Thomas Güttler guettliml@thomas-guettler.de wrote:
Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Diez
Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Disclaimer: Ich habe bisher keine praktische Erfahrung mit Pythons asyncio-Framework, aber verstehe auf einem relativ abstrakten Niveau, wie `async` und `await` verwendet werden. Ich habe einige Erfahrung mit asynchronen Code in Twisted.
Ich bin auch eher skeptisch, was asyncio angeht:
- Ich finde die `async`s und `await`s im Code unübersichtlich.
- Man muss sehr aufpassen, dass man nicht versehentlich synchron laufenden Code dazwischen hat. Siehe zum Beispiel https://whatisjasongoldstein.com/writing/im-too-stupid-for-asyncio/ In dem Fall funktioniert der Code zwar "im Prinzip" noch, aber die Nebenläufigkeit geht teilweise verloren.
- Durch mehrere Anläufe zu einer asyncio-API in Python ist die Sammlung dieser APIs sehr unübersichtlich geworden, siehe auch http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/
- Es ist hakelig, synchronen Code in einem asynchronen Kontext zu verwenden und umgekehrt.
- Ich vermute, dass das Nachdenken über solchen Code und vor allem das Debuggen kein Vergnügen ist.
Wie sind da eure Erfahrungen?
_Je nach Anwendungsfall_ gibt es diverse Alternativen. Mir fallen spontan ein:
- `concurrent.futures`, quasi ein Pool von Threads oder Prozessen, aber mit einer nützlichen API zum Zugriff auf die Ergebnisse. https://docs.python.org/3/library/concurrent.futures.html
- Worker-Threads, die über Queues kommunizieren. Keine Objekte aus keinem Thread verändern, wenn sie erst mal in einer Queue sind oder waren! Das gilt analog auch für Shared State mit dem Threadpool-Executor aus `concurrent.futures`.
- "Kleine" Prozesse, die über einen Broker (zum Beispiel RabbitMQ) kommunizieren. Diese Prozesse entsprechen in etwa den Worker-Threads aus dem vorherigen Punkt.
Mit den ersten beiden Ansätzen habe ich selbst gearbeitet, den dritten Ansatz habe ich in einem Projekt, in dem ich mitgearbeitet habe, im Einsatz gesehen.
Was fällt euch an weiteren Ansätzen ein?
Natürlich hat man immer noch die grundlegenden Probleme von Nebenläufigkeit, aber mir gefällt, dass hier die "Bausteine" aus relativ viel synchronem Code bestehen, der sich größtenteils getrennt testen und debuggen lässt.
Was man aus meiner Sicht möglichst vermeiden sollte, ist, mit Low-Level-APIs zu hantieren, siehe auch https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf Das ist ein alter Artikel, der aber meiner Meinung nach die Tücken solcher APIs nach wie vor gut erläutert.
Viele Grüße Stefan

Ich empfehle mal zu dem Thema einen sehr informativen Vortrag von Raymond Heutiger, „Keynote on Concurrency, PyBay 2017“: https://www.youtube.com/watch?v=9zinZmE3Ogk
Gruß,
Dinu
Am 16.01.2018 um 08:00 schrieb Stefan Schwarzer sschwarzer@sschwarzer.net:
On 2018-01-15 20:55, Sven R. Kunze wrote:
On 15.01.2018 17:07, Diez B. Roggisch wrote:
On 15. Jan 2018, at 16:46, Thomas Güttler guettliml@thomas-guettler.de wrote:
Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Diez
Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Disclaimer: Ich habe bisher keine praktische Erfahrung mit Pythons asyncio-Framework, aber verstehe auf einem relativ abstrakten Niveau, wie `async` und `await` verwendet werden. Ich habe einige Erfahrung mit asynchronen Code in Twisted.
Ich bin auch eher skeptisch, was asyncio angeht:
Ich finde die `async`s und `await`s im Code unübersichtlich.
Man muss sehr aufpassen, dass man nicht versehentlich
synchron laufenden Code dazwischen hat. Siehe zum Beispiel https://whatisjasongoldstein.com/writing/im-too-stupid-for-asyncio/ In dem Fall funktioniert der Code zwar "im Prinzip" noch, aber die Nebenläufigkeit geht teilweise verloren.
- Durch mehrere Anläufe zu einer asyncio-API in Python ist
die Sammlung dieser APIs sehr unübersichtlich geworden, siehe auch http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/
- Es ist hakelig, synchronen Code in einem asynchronen
Kontext zu verwenden und umgekehrt.
- Ich vermute, dass das Nachdenken über solchen Code und vor
allem das Debuggen kein Vergnügen ist.
Wie sind da eure Erfahrungen?
_Je nach Anwendungsfall_ gibt es diverse Alternativen. Mir fallen spontan ein:
- `concurrent.futures`, quasi ein Pool von Threads oder
Prozessen, aber mit einer nützlichen API zum Zugriff auf die Ergebnisse. https://docs.python.org/3/library/concurrent.futures.html
- Worker-Threads, die über Queues kommunizieren. Keine
Objekte aus keinem Thread verändern, wenn sie erst mal in einer Queue sind oder waren! Das gilt analog auch für Shared State mit dem Threadpool-Executor aus `concurrent.futures`.
- "Kleine" Prozesse, die über einen Broker (zum Beispiel
RabbitMQ) kommunizieren. Diese Prozesse entsprechen in etwa den Worker-Threads aus dem vorherigen Punkt.
Mit den ersten beiden Ansätzen habe ich selbst gearbeitet, den dritten Ansatz habe ich in einem Projekt, in dem ich mitgearbeitet habe, im Einsatz gesehen.
Was fällt euch an weiteren Ansätzen ein?
Natürlich hat man immer noch die grundlegenden Probleme von Nebenläufigkeit, aber mir gefällt, dass hier die "Bausteine" aus relativ viel synchronem Code bestehen, der sich größtenteils getrennt testen und debuggen lässt.
Was man aus meiner Sicht möglichst vermeiden sollte, ist, mit Low-Level-APIs zu hantieren, siehe auch https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf Das ist ein alter Artikel, der aber meiner Meinung nach die Tücken solcher APIs nach wie vor gut erläutert.
Viele Grüße Stefan _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de

Am 16. Januar 2018 08:00:29 MEZ schrieb Stefan Schwarzer sschwarzer@sschwarzer.net:
Siehe zum Beispiel https://whatisjasongoldstein.com/writing/im-too-stupid-for-asyncio/ siehe auch http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/
Super Beispiele dafür, das kooperatives Multitasking eigentlich in der Mottenkiste der Geschichte hätte stecken bleiben können. Aber gut. Wie unser erfahrener Kollege meinte, ab und zu muss man alten Wein in neuen Schläuchen verkaufen.
Wie sind da eure Erfahrungen?
_Je nach Anwendungsfall_ gibt es diverse Alternativen.
Wir setzen zur Zeit immer stärker auf die von dir erwähnte Daemon-Variante. Dies gar viele Vorteile: - Interpreter läuft bereits- - asynchrone Ausführung im Hintergrund - Notifications kommen über PostgreSQL - persistente Jobs mit Wiederholung von Notifications bei fehlgeschlagener Ausführung - automatisches Triggern von Ausführungen durch PostgreSQL-Trigger - Prozess-Management komplett ausgelagert an Linux
Die nächsten Schritte werden wahrscheinlich sein, keinen lokalen Daemon für jeden Job-Typ mehr zu verwenden, sondern einfach einen Webserver. Der kann Worker-Management (Parallelität) nämlich schon. Und hat weitere Vorteile: - Adressierbarkeit von Funktionalität - Remote-Ausführung - standardisiertes Protokoll im Fehlerfall
Oder man baut das alles selber (not-invented-here-syndrome) und nimmt asyncio. 😉😉
Sven

Von meinem iPad gesendet
Am 16.01.2018 um 08:00 schrieb Stefan Schwarzer sschwarzer@sschwarzer.net:
On 2018-01-15 20:55, Sven R. Kunze wrote: On 15.01.2018 17:07, Diez B. Roggisch wrote:
On 15. Jan 2018, at 16:46, Thomas Güttler guettliml@thomas-guettler.de wrote:
Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Diez
Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Disclaimer: Ich habe bisher keine praktische Erfahrung mit Pythons asyncio-Framework, aber verstehe auf einem relativ abstrakten Niveau, wie `async` und `await` verwendet werden. Ich habe einige Erfahrung mit asynchronen Code in Twisted.
Ich bin auch eher skeptisch, was asyncio angeht:
Ich finde die `async`s und `await`s im Code unübersichtlich.
Man muss sehr aufpassen, dass man nicht versehentlich
synchron laufenden Code dazwischen hat. Siehe zum Beispiel https://whatisjasongoldstein.com/writing/im-too-stupid-for-asyncio/ In dem Fall funktioniert der Code zwar "im Prinzip" noch, aber die Nebenläufigkeit geht teilweise verloren.
Ist ein Bug. Ich halte das im Vergleich zu Races in MT Umgebungen nicht für schlimmer.
- Durch mehrere Anläufe zu einer asyncio-API in Python ist
die Sammlung dieser APIs sehr unübersichtlich geworden, siehe auch http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/
- Es ist hakelig, synchronen Code in einem asynchronen
Kontext zu verwenden und umgekehrt.
- Ich vermute, dass das Nachdenken über solchen Code und vor
allem das Debuggen kein Vergnügen ist.
Wie sind da eure Erfahrungen?
Threaded Code ist da ja auch notorisch schwierig. Bestimmte Races verschwinden schon, wenn man nur Print statements oder anderes Logging einbaut. Etc.
_Je nach Anwendungsfall_ gibt es diverse Alternativen. Mir fallen spontan ein:
- `concurrent.futures`, quasi ein Pool von Threads oder
Prozessen, aber mit einer nützlichen API zum Zugriff auf die Ergebnisse. https://docs.python.org/3/library/concurrent.futures.html
Wo genau der Unterschied zwischen einem Executor und einem Mainloop ist, insbesondere wenn man bei den Futures eine completion callback angibt, ist kaum erkennbar.
- Worker-Threads, die über Queues kommunizieren. Keine
Objekte aus keinem Thread verändern, wenn sie erst mal in einer Queue sind oder waren! Das gilt analog auch für Shared State mit dem Threadpool-Executor aus `concurrent.futures`.
- "Kleine" Prozesse, die über einen Broker (zum Beispiel
RabbitMQ) kommunizieren. Diese Prozesse entsprechen in etwa den Worker-Threads aus dem vorherigen Punkt.
Das ist ja erstmal komplett unabhängig davon, wie du deine Anwendung selbst strukturierst. Auf deine Arbeitsaufträge aus der Queue kannst du genauso asynchron warten.
Ich will hier asyncio nicht über den grünen Klee loben. Aber ich sehe viel gefrickelten Code, der mit Queues und Threads genau das nachbaut, was einem ein eventloop wie asyncio einem schon gibt. Und dann vernünftig. Von wegen not invented here....
Die Frage ist meines Erachtens, ob die eingeführten Sprachkonstrukte es erlauben klareren Code zu schreiben, als das asynchrone Paradigma vorher mit callbacks und deferrables erfordert hat. Finde ich, ja.
Und den Wert asynchroner Programmierung bezüglich Skalierung und Resourcenverbrauch belegen Server wie NGINX.
LG Diez
Mit den ersten beiden Ansätzen habe ich selbst gearbeitet, den dritten Ansatz habe ich in einem Projekt, in dem ich mitgearbeitet habe, im Einsatz gesehen.
Was fällt euch an weiteren Ansätzen ein?
Natürlich hat man immer noch die grundlegenden Probleme von Nebenläufigkeit, aber mir gefällt, dass hier die "Bausteine" aus relativ viel synchronem Code bestehen, der sich größtenteils getrennt testen und debuggen lässt.
Was man aus meiner Sicht möglichst vermeiden sollte, ist, mit Low-Level-APIs zu hantieren, siehe auch https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf Das ist ein alter Artikel, der aber meiner Meinung nach die Tücken solcher APIs nach wie vor gut erläutert.
Viele Grüße Stefan _______________________________________________ python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de

On 2018-01-16 22:47, Diez B. Roggisch wrote:>> Am 16.01.2018 um 08:00 schrieb Stefan Schwarzer sschwarzer@sschwarzer.net:
- Man muss sehr aufpassen, dass man nicht versehentlich
synchron laufenden Code dazwischen hat. Siehe zum Beispiel https://whatisjasongoldstein.com/writing/im-too-stupid-for-asyncio/ In dem Fall funktioniert der Code zwar "im Prinzip" noch, aber die Nebenläufigkeit geht teilweise verloren.
Ist ein Bug. Ich halte das im Vergleich zu Races in MT Umgebungen nicht für schlimmer.
Ich glaube, hier spielt sehr stark mit rein, wie gut man (in beiden Fällen) den Code verstehen und die Bugs vermeiden oder beheben kann. Die "Gelegenheiten" für Race Conditions sind in Multithread- oder Multiprozess-basiertem Code sicher häufiger und subtiler.
Race Conditions und Deadlocks kann man auch in asyncio- Programmen haben, je nachdem ob/wie die nebenläufigen Programmteile voneinander abhängen.
- Durch mehrere Anläufe zu einer asyncio-API in Python ist
die Sammlung dieser APIs sehr unübersichtlich geworden, siehe auch http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/
- Es ist hakelig, synchronen Code in einem asynchronen
Kontext zu verwenden und umgekehrt.
- Ich vermute, dass das Nachdenken über solchen Code und vor
allem das Debuggen kein Vergnügen ist.
Wie sind da eure Erfahrungen?
Threaded Code ist da ja auch notorisch schwierig. Bestimmte Races verschwinden schon, wenn man nur Print statements oder anderes Logging einbaut. Etc.
Das stimmt. Mich würden allerdings eher Erfahrungen mit asyncio-Code interessieren, nicht mit multithreaded Code. :-)
_Je nach Anwendungsfall_ gibt es diverse Alternativen. Mir fallen spontan ein:
- `concurrent.futures`, quasi ein Pool von Threads oder
Prozessen, aber mit einer nützlichen API zum Zugriff auf die Ergebnisse. https://docs.python.org/3/library/concurrent.futures.html
Wo genau der Unterschied zwischen einem Executor und einem Mainloop ist, insbesondere wenn man bei den Futures eine completion callback angibt, ist kaum erkennbar.
Ich vermeide Callbacks bei Futures (und auch sonst), wenn es geht. Was ich dagegen sehr mag, ist der `as_completed`-Iterator, https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures... Ich denke, diese vielleicht unscheinbare Funktion macht viel für die "User Experience" aus.
Die Frage ist meines Erachtens, ob die eingeführten Sprachkonstrukte es erlauben klareren Code zu schreiben, als das asynchrone Paradigma vorher mit callbacks und deferrables erfordert hat. Finde ich, ja.
Ich würde asyncio-Code ziemlich sicher Callback-basiertem Code vorziehen. Aber der Punkt ist ja, ob bzw. unter welchen Umständen es außer "asyncio mit Callbacks" und "asyncio mit `async`/`await`" welche Alternativen gibt.
Und den Wert asynchroner Programmierung bezüglich Skalierung und Resourcenverbrauch belegen Server wie NGINX.
Dass das prinzipiell funktioniert, ist klar. Die Frage ist halt, wann ist es der "beste" Ansatz?
Ich glaube, mich persönlich stört am meisten, dass man synchron geschriebenen Code und mit `async`/`await` geschriebenen Code nicht gut verbinden kann. Beziehungsweise das sieht immer irgendwie zusammengefrickelt aus.
Für mich sieht `async`/`await`-Code in Python quasi wie eine andere Programmiersprache aus.
Viele Grüße Stefan

Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Dir ist schon klar, das Filedeskriptoren die Basis von asyncio sind?
Sockets sind meiner Meinung nach ein viel besseres Abstraktionsniveau als meinen Quellcode überall mit diesem dämlichen async und await zu spicken.
Das einzige, was hier fehlt, wäre ein spezieller Typ von File-Deskriptor, der entsprechend in der Lage ist HTTP zu sprechen.
Du moechtest also einen HTTP-Server in den Kernel verlagern? Wo wir dabei sind, gleich noch ne RDBMS-engine?
Wenn du nochmal auf der Suche nach gequirltem Unsinn bist: das waere welcher.
*Kooperatives Multitasking ist einfach nur Schrott.* Ich habe mich neulich mit einem Mitt-Fünziger unterhalten, der asyncio in seinem neusten Projekt einsetzt, und habe ihn gefragt, wie er es findet und ob es sich irgendwie zu kooperativen Multitasking aus Mainframe-Zeiten (was bekanntlich aus der Mode gekommen ist) unterscheidet.
Er sagte folgendes:
naaa it’s the same thing .. like a loop u know .. marketing .. blah blah .. some technology went out from window .. and back from doors .. like something new *we call that progress* .. but is something like reinvent the wheel for sell something ;) ppl need “new” words, “new” languages .. to feel young :-)
“”” Alter Mann erklaert Technologie, die nach seiner eigenen Sturm-und-Drang-Phase entwickelt wurde, fuer ueberfluessig.
Auch interessant fuer sie: Sack Reis in China umgefallen. “””
Selbst ich erinnere mich noch daran, wie ich im Kurs Betriebssysteme dieses Thema einfach nur zum Reihern fande. Um Scheduling soll sich gefälligst das Betriebssystem kümmern. Das können die Leute aus der Ecke eh viel besser als ich und ich befürchte die meisten, die asyncio einsetzen überschätzen ihre Fähigkeiten diesbzgl. total.
Und wo genau tut es das nicht? Ob du nun in einem blockierenden Call stehst, und das OS deinen Thread schlafen legt, bis was da ist, oder das gleiche OS dich wieder aufweckt, wenn auf besagtem FD Daten anliegen, dann ist das in beiden Faellen vom OS geplant.
Und die gleichen Leute, die zur Selbstueberschaetzung neigen wenn sie asynchron programmieren sollen (und das dank syntaktischem Zucker ohne dabei in die Callback-Hoelle hinabzusteigen) sind ploetzlich befaehigt, Nebenlaeufigken Code mit seinem ganzen Sack Problemen zu meistern? Sicher.
Ereignisbasierte Programmierung ist gang und gaebe, und jeder, der schon mal eine GUI erstellen musste, kann gar nicht anders, als damit zu arbeiten. Aber weil es statt “addListener” oder “signal/slot” async/await heisst, ist es ploetzlich eine schlechte Idee….
Sven
PS: der liebe Mitt-Fünfziger meinte am Ende noch er müsste jetzt nochmal schauen, warum sich in seiner Anwendung irgendwelche laufenden Coroutinen nicht beenden. Eigentlich sollte doch das die Programmiersprache für ihn tun... naja, ein selbst-gehäkeltes Betriebssystem ist halt doch nicht so einfach, wie manche hier auf den Python-Mailinglisten behaupten.
Gottlob erledigt die gleiche Programmiersprache nebenlaeufige Threads ja mit traumwandlerischer Sicherheit. ZB indem sie Module schon garbage-Collected, waehrend ein anderer Thread in einem Destruktor versucht, just jenes Modul zu nutzen, um irgendwelche Resourcen freizugeben. Was so alles behauptet wird auf Mailinglisten...
PPS: Übersetzung von oben
naaa es ist dasselbe .. eine Art Schleife du weißt schon .. Marketing .. bla bla .. manche Technologie wurde aus dem Fenster geworfen .. und kam postwendend durch die Tür wieder herein .. als etwas neues *wir nennen das dann Fortschritt* .. aber das ist wie Rad neu zu erfinden um etwas zu verkaufen ;) Leute brauchen "neue" Wörter, "neue" Sprachen .. um sich jung zu fühlen :-)
python-de maillist - python-de@python.org https://mail.python.org/mailman/listinfo/python-de

Sven R. Kunze schrieb am 15.01.2018 um 20:55:
On 15.01.2018 17:07, Diez B. Roggisch wrote:
On 15. Jan 2018, at 16:46, Thomas Güttler wrote: Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Sockets sind meiner Meinung nach ein viel besseres Abstraktionsniveau als meinen Quellcode überall mit diesem dämlichen async und await zu spicken.
Ich verstehe nicht, warum das ein Argument dafür sein sollte, sich seinen eigenen I/O-Loop zu schreiben. AsyncIO wurde exakt und einzig und allein aus dem Grund geschrieben, dass es bereits zu viele davon gab, die alle nicht miteinander kompatibel waren. Wenn Thomas also jetzt darüber nachdenkt, sein handgedrechseltes select() noch weiter zu einem solchen auszubauen, dann nehme ich das als Anreiz, auf die vergleichbaren Fehler anderer hinzuweisen, aus denen bereits nachhaltig Lehren gezogen wurden.
Ein hübsches Ergebnis ist jetzt z.B., dass das Tornado Web-Framework in der Version 5 komplett auf asyncio umziehen wird und in der Folgeversion den eigenen I/O-Loop über den Jordan jagt. Das große Aufräumen hat begonnen.
Und eine zweite Implementierung von asyncio gibt es mit uvloop auch schon, so dass jetzt auf beiden Seiten der Schnittstelle die freie Auswahl besteht. AsyncIO ist auf dem besten Weg, für die Welt des kooperativen Multitaskings so etwas zu werden wie WSGI es seit Jahren schon für synchrone Web-Frameworks ist.
Stefan

On 16.01.2018 19:55, Stefan Behnel wrote:
Ich verstehe nicht, warum das ein Argument dafür sein sollte, sich seinen eigenen I/O-Loop zu schreiben.
Weil, wie du an anderen Antworten hier im Thread und auch an verlinkten Resourcen merkst, dies ein riesengroßes Problem darstellt.
Da verstehe ich nicht, warum nicht proaktiv auf dieses Problem eingegangen wird. Kein normaler Programmierer will alle seine Bibliotheken auf eine zweite Programmiersprache umschreiben. Das ist das Zwei-Welten-Problem bei asyncio. Stattdessen höre ich immer nur: "du machst das falsch", "asyncio ist die Zukunft" etc.
Darüber hinaus sehe ich noch ein weiteres Problem: wenn async+await tatsächlich die Zukunft darstellen sollen, dann sollte ja auch deren Verwendung ziemlich stark zunehmen. Wenn dann also jeder Funktionsaufruf mit einem await versehen und jede Funktionsdefinition mit einem async versehen worden ist, dann macht es doch überhaupt keinen Sinn mehr?
Mag sein, dass ich hier zu weit in die Zukunft denke, aber eine technische Mischwelt halte ich für fast unmöglich und wurde mir auch von vielen erfahrenen Entwicklern bestätigt. Entweder async-Welt oder sync-Welt. Allerdings sagt mir meine Erfahrung: man braucht einen sanften Migrationsweg oder man hat gar keine Migration. Und das sehe ich noch als riesiges Problem.
AsyncIO wurde exakt und einzig und allein aus dem Grund geschrieben, dass es bereits zu viele davon gab, die alle nicht miteinander kompatibel waren. Wenn Thomas also jetzt darüber nachdenkt, sein handgedrechseltes select() noch weiter zu einem solchen auszubauen, dann nehme ich das als Anreiz, auf die vergleichbaren Fehler anderer hinzuweisen, aus denen bereits nachhaltig Lehren gezogen wurden.
select ist doch eine Standardfunktion. Was ist daran handgedrechselt? Meinst du die Schleife in der das select vorkommt?
Ein hübsches Ergebnis ist jetzt z.B., dass das Tornado Web-Framework in der Version 5 komplett auf asyncio umziehen wird und in der Folgeversion den eigenen I/O-Loop über den Jordan jagt. Das große Aufräumen hat begonnen.
Und eine zweite Implementierung von asyncio gibt es mit uvloop auch schon, so dass jetzt auf beiden Seiten der Schnittstelle die freie Auswahl besteht. AsyncIO ist auf dem besten Weg, für die Welt des kooperativen Multitaskings so etwas zu werden wie WSGI es seit Jahren schon für synchrone Web-Frameworks ist.
Das ist schön für das kooperative Multitasking. Aber die Probleme beim Übergang von einer in die andere Welt bleiben und die sollte man nicht totreden oder schweigen.
Sven

Sven R. Kunze schrieb am 17.01.2018 um 19:36:
On 16.01.2018 19:55, Stefan Behnel wrote:
Sven R. Kunze schrieb am 15.01.2018 um 20:55:
Sockets sind meiner Meinung nach ein viel besseres Abstraktionsniveau als meinen Quellcode überall mit diesem dämlichen async und await zu spicken.
Ich verstehe nicht, warum das ein Argument dafür sein sollte, sich seinen eigenen I/O-Loop zu schreiben.
Weil, wie du an anderen Antworten hier im Thread und auch an verlinkten Resourcen merkst, dies ein riesengroßes Problem darstellt.
Da verstehe ich nicht, warum nicht proaktiv auf dieses Problem eingegangen wird. Kein normaler Programmierer will alle seine Bibliotheken auf eine zweite Programmiersprache umschreiben. Das ist das Zwei-Welten-Problem bei asyncio.
Und anstatt meinen Code speziell für asyncio zu schreiben, ist es dann besser, ihn speziell für select() zu schreiben? Der "Vorteil" erschließt sich mir absolut nicht. Bei asyncio brauche ich den meisten Code *gar nicht* zu schreiben, weil es für fast alles schon fertige Tools und Bibliotheken gibt, die ich dank der allgemeinen Schnittstelle einfach ineinander stöpseln kann.
Bei einem blanken select() (oder epoll() usw.) fange ich dagegen komplett bei Null an, weil ich damit faktisch auf der Socket-Ebene festhänge, also ganz, ganz unten. Dann brauche ich erst einmal Tools, die inkrementell auf Sockets lesen und schreiben können, und die muss ich dann auch noch mühsam orchestrieren. Also muss ich genau die (fehleranfällige) Arbeit selbst machen, die mir ein I/O-Loop fertig (und fertig debuggt) abnimmt. Für nichts anderes ist doch ein I/O-Loop da.
Wenn dich jemand bittet, eine Webseite zu entwickeln, ist dann dein erster Schritt, das HTTP-Protokoll zu implementieren?
Ganz ehrlich, die Begriffe "asyncio", "async/await" und "kooperatives Multitasking" haben zwar durchaus etwas miteinander zu tun, sind aber nicht gleichbedeutend. Sie sind alle nur (sehr allgemeine) Werkzeuge, mit denen sich alles Mögliche machen lässt. Deine bisherige Kritik scheint sich an einer ziemlich schmalen Kombination der drei Begriffe aufzuhängen, die weder Nutzen noch Anwendungsbereich auch nur annähernd abdeckt.
Stefan

On 17.01.2018 22:49, Stefan Behnel wrote:
Und anstatt meinen Code speziell für asyncio zu schreiben, ist es dann besser, ihn speziell für select() zu schreiben? Der "Vorteil" erschließt sich mir absolut nicht. Bei asyncio brauche ich den meisten Code *gar nicht* zu schreiben, weil es für fast alles schon fertige Tools und Bibliotheken gibt, die ich dank der allgemeinen Schnittstelle einfach ineinander stöpseln kann.
Sprichst du aus eigener Erfahrung? Jemand anderes im Thread hatte nach genau solcher Erfahrung gefragt. Für mich liest sich das, was du da schreibst, leider immer noch nach einer Wiederholung des Marketings. Quellcode wäre gut.
Den asyncio-Quellcode, den ich bisher immer zu Gesicht bekommen habe, involvierte ab einer bestimmten Komplexitätsgrenze stets Threads. Wo ich mich dann naturgemäßg wieder frage, ob ich da im falschen Film bin, wo doch gerne so über Threads geschimpft wird.
Das Ineinander-Stöpseln, was ich da erlebt habe, war dann mit so einer riesigen Menge Boilerplate verbunden, dass ich diese Implementierung nicht wirklich ernstnehmen konnte. Für mich war es eher nur Show-Case anstelle einer Implementierung mit Mehrwert. Da wäre ich jetzt wirklich interessiert an realem Beispielen.
Bei einem blanken select() (oder epoll() usw.) fange ich dagegen komplett bei Null an, weil ich damit faktisch auf der Socket-Ebene festhänge, also ganz, ganz unten. Dann brauche ich erst einmal Tools, die inkrementell auf Sockets lesen und schreiben können, und die muss ich dann auch noch mühsam orchestrieren. Also muss ich genau die (fehleranfällige) Arbeit selbst machen, die mir ein I/O-Loop fertig (und fertig debuggt) abnimmt. Für nichts anderes ist doch ein I/O-Loop da.
Ich bin durchaus in den allgemeinen Prinzipien der Software-Entwicklung bewandert.
Dennoch, die Problempunkte, die ich und viele andere immer wieder anbringen, werden auch hier nicht wirklich entkräftet und du gehst nicht ansatzweise darauf.
Natürlich ist es schön, Code wiederzuverwenden, das gilt aber sowohl für den tollen neuen asyncio-Code als auch für den bereits geschriebenen alten langweiligen sync-Code. Natürlich ist es schön, nicht an unterster Ebenen festzuhängen, aber async und await sind jetzt nicht wirklich high-level und select nimmt einem mega viel Arbeit, die ich nicht programmieren möchte, ab.
Solange wir nur auf dem Niveau "meins ist besser" argumentieren, können wir hier auch aufhören. Anderenfalls würde ich gerne hören, wie sich die asyncio-Gemeinde die Lösung des Zwei-Welten-Problems vorstellt. Zum Beispiel anhand von WSGI.
Sven

On Thursday, 18 January, 2018 08:02 PM, Sven R. Kunze wrote:
Den asyncio-Quellcode, den ich bisher immer zu Gesicht bekommen habe, involvierte ab einer bestimmten Komplexitätsgrenze stets Threads. Wo ich mich dann naturgemäßg wieder frage, ob ich da im falschen Film bin, wo doch gerne so über Threads geschimpft wird.
Das Ineinander-Stöpseln, was ich da erlebt habe, war dann mit so einer riesigen Menge Boilerplate verbunden, dass ich diese Implementierung nicht wirklich ernstnehmen konnte.
Würde ich nur solchen Code zu sehen bekommen, ginge es mir wohl wie dir. Von daher kann ich jetzt zumindest deine "Begeisterung" für asyncio verstehen. ;-)
Natürlich ist es schön, nicht an unterster Ebenen festzuhängen, aber async und await sind jetzt nicht wirklich high-level und select nimmt einem mega viel Arbeit, die ich nicht programmieren möchte, ab.
Nein, async und await sind nicht unbedingt high-level, aber eine höhere Abstraktion als select(). Ich für meinen Teil bin kein Experte in Sockets, select, poll, ... Aus meiner Sicht sind async und await exakt die richtige Abstraktion. Daß das Problem nicht vollständig versteckt wird, liegt - zumindest meinem Verständnis nach - daran, daß es ganz einfach nicht geht. Ich hab' Ansätze in Ruby (On Rails) gesehen, da würdest du mit Freuden asnyc/await nehmen. ;-)
Anderenfalls würde ich gerne hören, wie sich die asyncio-Gemeinde die Lösung des Zwei-Welten-Problems vorstellt. Zum Beispiel anhand von WSGI.
Meinst du mit Zwei-Welten-Problem das Problem, den Übergang von asnyc Code zu sync bzw. umgekehrt? Exakt das löst async/await in meinen Augen exakt so gut, wie es in Python auf Grund der Natur der Sprache eben geht. Die zwei Welten sind nunmal unterschiedlich. Klar kannst du eine Sprache wie Go (bezogen auf deine andere Nachricht) explizit für das Szenario designen. Haskellcompiler können auch sehr coole Sachen machen. Die Sprachen funktionieren aber auch anders.
Der Hauptkritikpunkt, den ich gelten lassen würde, ist, daß in den meisten Beispielen eben exakt der Übergang ausgespart wird. Ich hab' mir beim ersten mal auch den Wolf gesucht, bis ich die richtigen Funktionen zur Hand hatte. Und um sie zu benutzen, muß man schon ein solides Bild davon haben, was da passiert.
Nochmal mein Fazit: Ich halte async/await für die beste Abstraktion, die in Python geht. Was dich nicht davon abhalten soll, irgendwas eigenes zu bauen. In den meisten Fällen unterstelle ich aber, daß die Leute, die sich damit auf die Nase legen, ihre Hausaufgaben nicht gemacht haben.
Grüße, Achim

On 18.01.2018 21:31, Achim Domma wrote:
Würde ich nur solchen Code zu sehen bekommen, ginge es mir wohl wie dir. Von daher kann ich jetzt zumindest deine "Begeisterung" für asyncio verstehen. ;-)
:)
Mir ist in erster Linie wichtig, dass es funktioniert. Voreilige Optimierungen habe ich schon zur Genüge gesehen. Dafür hat es schon zu oft geklemmt, weil jemand meinte besonders schlau sein zu müssen. Wenn's brennt, dann muss es geradeaus gehen.
Und im Endeffekt ist asyncio nichts anderes als eine Optimierung. Ich denke, da sind wir uns einig.
Nein, async und await sind nicht unbedingt high-level, aber eine höhere Abstraktion als select().
Vielleicht hinkt der Vergleich auch etwas.
EventLoop.run_forever und while True: select(...) sind miteinander vergleichbar.
async/await haben kein wirkliches Pendant.
Anderenfalls würde ich gerne hören, wie sich die asyncio-Gemeinde die Lösung des Zwei-Welten-Problems vorstellt. Zum Beispiel anhand von WSGI.
Meinst du mit Zwei-Welten-Problem das Problem, den Übergang von asnyc Code zu sync bzw. umgekehrt? Exakt das löst async/await in meinen Augen exakt so gut, wie es in Python auf Grund der Natur der Sprache eben geht. Die zwei Welten sind nunmal unterschiedlich.
Na, das würde ich nicht so unterschreiben wollen.
Deswegen auch das Beispiel mit dem WSGI. Richtig altmodisch klassisch hat man da einen Apache, der 20 WSGI-Worker mit Django zum Beispiel moderiert. Jeder Worker läuft in einem separatem Prozess. Dateien liefert der Apache alleine über eine entsprechende Kernel-Schnittstelle aus. Die Datenbank ist angebunden, 1 Worker 1 Connection üblicherweise. Wenn die Datenbank-Anfrage länger dauert, ist der Worker blockiert und kann keine anderen Anfragen beantworten. Es kann auch 10 bis 100 DB-Anfragen für einen WSGI-Request erfolgen, oder auch mal 10.000, wenn der Programmierer Mist gebaut hat. :D
Jetzt die blöden Fragen:
- wie fummelt man da asyncio hinein? Ich fürchte, das würde überhaupt nicht gehen. "Mal eben ausprobieren" wird hart. - *was wäre eigentlich die Zielsetzung?* Angenommen, man hätte das doch irgendwie geschafft: - wie stellt man sicher, dass, wenn die Abarbeitung eines Requests furios in, z.B., einem MemoryError scheitert, nicht auf einmal die anderen 100 Anfragen den Bach runtergehen? - wie stellt man sicher, dass nicht 1 fehlerhaft implementierter View (das ist das Ding, was für einen Request eine Response erarbeitet), alle anderen 100 Anfragen blockiert - await vergessen, oder so?
Die Nachteile kooperativen Multitaskings bleiben bestehen, auch wenn diese totgeschwiegen werden. ;-)
Ich kann mir gut vorstellen, dass asyncio bei einem kleinen, niedlichen, perfekt abgeschlossenen Projekt, wie einem HTTP-File-Server (wie z.B. nginx) super gut funktioniert. Da ist alles gut abgestimmt, niemand tritt sich gegenseitig auf die Füße und es gibt eig. nur 1 Funktion -> serve_file. Aber die meisten Web-Anwendungen, die Geschäftslogiken abbilden, sind dann doch eher ziemlich kompliziert/komplex und müssen ständig erweitert werden, wo ich mir gut vorstellen kann, dass man sich mit kooperativen Multitasking ziemlich sicher ins Knie schießt; auch je größer das Team wird. Da fehlen mir die Sicherungsmaßnahmen, die man bei einem separaten Prozess einfach hat.
Klar kannst du eine Sprache wie Go (bezogen auf deine andere Nachricht) explizit für das Szenario designen.
Ich hoffe ja noch immer bisschen auf golang-Einfluss. :D Aber bei vielen hier steht da noch das Ego im Weg.
Der Hauptkritikpunkt, den ich gelten lassen würde, ist, daß in den meisten Beispielen eben exakt der Übergang ausgespart wird. Ich hab' mir beim ersten mal auch den Wolf gesucht, bis ich die richtigen Funktionen zur Hand hatte. Und um sie zu benutzen, muß man schon ein solides Bild davon haben, was da passiert.
Ist man von Python eben nicht gewöhnt. So etwas fühlt sich dann an, wie wenn man von Python Ganzzahlen zu C int32 wechseln muss.
Nochmal mein Fazit: Ich halte async/await für die beste Abstraktion, die in Python geht.
Ich würde diese Aussage insoweit relativieren, dass es zur Zeit als die von vielen beste Abstraktion angesehen wird.
Sven

On Thursday, 18 January, 2018 11:11 PM, Sven R. Kunze wrote:
Mir ist in erster Linie wichtig, dass es funktioniert. Voreilige Optimierungen habe ich schon zur Genüge gesehen. Dafür hat es schon zu oft geklemmt, weil jemand meinte besonders schlau sein zu müssen. Wenn's brennt, dann muss es geradeaus gehen.
Was für mich ein guter Grund wäre, asyncio zu benutzen. Bei Code in der Standardlib setze ich einfach mal voraus, daß da intelligente Menschen am Werk waren. Nochmal das Disclaimer: Ich setze voraus, daß ich weiß, was ich tue. Wenn sich jemand mutwillig selbst in den Fuß schießt, ist das nicht mein Problem.
Vielleicht ist das ein netter Blickwinkel: Für mich ist asyncio ein "power feature" für sehr spezielle Anwendungsfälle. Wenn ich die habe, nutze ich es, ansonsten nicht. Wenn's jemand aus Selbstzwecke oder Unwissenheit nutzt, ist das nicht mein Problem.
Würde ich in einer Umgebung programmieren wollen, in der man mir meine Mündigkeit (= Option, mir im Glauben smart zu sein, selbst ins Knie zu schießen) nimmt, würde ich Java programmieren. ;-)
Und im Endeffekt ist asyncio nichts anderes als eine Optimierung. Ich denke, da sind wir uns einig.
Nein und ich bin mir nicht sicher, wie du das meinst. Also ich benutze asyncio, um meinen Code zu optimieren.
Deswegen auch das Beispiel mit dem WSGI. Richtig altmodisch klassisch hat man da einen Apache, der 20 WSGI-Worker mit Django zum Beispiel moderiert. Jeder Worker läuft in einem separatem Prozess. Dateien liefert der Apache alleine über eine entsprechende Kernel-Schnittstelle aus. Die Datenbank ist angebunden, 1 Worker 1 Connection üblicherweise. Wenn die Datenbank-Anfrage länger dauert, ist der Worker blockiert und kann keine anderen Anfragen beantworten. Es kann auch 10 bis 100 DB-Anfragen für einen WSGI-Request erfolgen, oder auch mal 10.000, wenn der Programmierer Mist gebaut hat. :D
Deswegen meine Aussage, daß die Welten nunmal unterschiedlich sind. Wenn du mit diesem klassischen WSGI Modell leben kannst, ist das toll. Dann würde ich auch dabei bleiben. Es gibt aber nunmal andere Anwendungsfälle, die quasi 100% IO gebunden sind und die viele Connections gleichzeitig aufhalten müssen.
Jetzt die blöden Fragen:
- wie fummelt man da asyncio hinein? Ich fürchte, das würde überhaupt
nicht gehen. "Mal eben ausprobieren" wird hart.
Zumindest ist die Chance, daß etwas sinnvolles dabei raus kommt, relativ gering.
- *was wäre eigentlich die Zielsetzung?*
Gute Frage, die man natürlich vorher klären sollte. Die Zielsetzung könnte aber z.B. sein, 100k Websocketconnections mit möglichst wenig Resourcen bedienen zu wollen.
Angenommen, man hätte das doch irgendwie geschafft:
- wie stellt man sicher, dass, wenn die Abarbeitung eines Requests
furios in, z.B., einem MemoryError scheitert, nicht auf einmal die anderen 100 Anfragen den Bach runtergehen?
Gar nicht. Und?
- wie stellt man sicher, dass nicht 1 fehlerhaft implementierter View
(das ist das Ding, was für einen Request eine Response erarbeitet), alle anderen 100 Anfragen blockiert - await vergessen, oder so?
Wie stellt man sicher, daß Software keine Bugs hat? ;-) Ja, das kann passieren, zeigt sich meiner Erfahrung nach aber ziemlich schnell.
Die Nachteile kooperativen Multitaskings bleiben bestehen, auch wenn diese totgeschwiegen werden. ;-)
Keine Ahnung, ob die totgeschwiegen werden. Ich bleibe da bei meinem bodenständigen "man sollte wissen, was man tut". Vermutlich wird niemand eine GUI basierend auf asyncio entwickeln wollen. Massiv paralleles IO für Batchprozesse ist jetzt in der Regel nicht soooo komplex und ich bezweifle, daß es eine ähnlich einfache Lösung gibt, die gleiche Performance auf Multicoremaschinen zu erreichen.
Ich kann mir gut vorstellen, dass asyncio bei einem kleinen, niedlichen, perfekt abgeschlossenen Projekt, wie einem HTTP-File-Server (wie z.B. nginx) super gut funktioniert. Da ist alles gut abgestimmt, niemand tritt sich gegenseitig auf die Füße und es gibt eig. nur 1 Funktion -> serve_file.
Kleine ins sich geschlossene Projekt, die eine Aufgabe gut erfüllen, sind eine tolle Sache.
Aber die meisten Web-Anwendungen, die Geschäftslogiken abbilden, sind dann doch eher ziemlich kompliziert/komplex und müssen ständig erweitert werden, wo ich mir gut vorstellen kann, dass man sich mit kooperativen Multitasking ziemlich sicher ins Knie schießt; auch je größer das Team wird. Da fehlen mir die Sicherungsmaßnahmen, die man bei einem separaten Prozess einfach hat.
Natürlich. Aber warum sollte ich für sowas asyncio verwenden wollen?
Ich hoffe ja noch immer bisschen auf golang-Einfluss. :D Aber bei vielen hier steht da noch das Ego im Weg.
Auch wenn Ego in unserer Branche ein Problem ist, sehe ich das hier anders. Die Interna von Python machen es schwer bzw. unmöglich, Code "einfach so" schlafen zu legen. Es gibt genug Vorträge von Guido bzgl. Entfernen des GIL, in denen er das besser erklärt, als ich es aus dem Kopf kann. Bei Armin Rigo kann man nichts ausschließen und evtl. baut er ja ein Python mit "software transactional memory" und ohne GIL. Aber bis dahin ist mein Verständnis, daß das, was du gerne hättest, ganz einfach nicht geht.
Der Hauptkritikpunkt, den ich gelten lassen würde, ist, daß in den meisten Beispielen eben exakt der Übergang ausgespart wird. Ich hab' mir beim ersten mal auch den Wolf gesucht, bis ich die richtigen Funktionen zur Hand hatte. Und um sie zu benutzen, muß man schon ein solides Bild davon haben, was da passiert.
Ist man von Python eben nicht gewöhnt. So etwas fühlt sich dann an, wie wenn man von Python Ganzzahlen zu C int32 wechseln muss.
Deswegen oben mein Verweis auf "power feature". Der durchschnittliche Entwickler wird nie einen Grund haben, asyncio zu verwenden. Viele werden es versuche, weil es möglich ist. Ähnlich wie bei meta classes. Daß ein Feature mißbraucht wird, macht es aber nicht per se schlecht.
Nochmal mein Fazit: Ich halte async/await für die beste Abstraktion, die in Python geht.
Ich würde diese Aussage insoweit relativieren, dass es zur Zeit als die von vielen beste Abstraktion angesehen wird.
Damit kann ich leben. ;-) Gerne höre ich mir auf der nächsten Europython deinen Vortrag darüber an, wie man's besser machen könnte. Das hätte quasi schon Tradition. :-)
Nur so aus Neugier: Es gibt Sprachen wie Go, die u.a. für diese Anwendungsfälle gebaut wurden. Klar sind sie in dem Bereich besser. Hast du dir mal Lösungen in den anderen Web/Script-Sprachen angeschaut? Ruby, Php, ... Hast du da irgendwas gesehen, daß eleganter wäre, also das asyncio von Python?
Grüße, Achim

On 18.01.2018 23:50, Achim Domma wrote:
- *was wäre eigentlich die Zielsetzung?*
Gute Frage, die man natürlich vorher klären sollte. Die Zielsetzung könnte aber z.B. sein, 100k Websocketconnections mit möglichst wenig Resourcen bedienen zu wollen.
Das wäre doch mal ein sinnvoller Ansatz, und darauf wollte ich hinaus. WSGI und asyncio sind für mich nur Werkzeug und da bin ich vollkommen emotionslos und bewerte nach objektiven Kriterien.
- wie stellt man sicher, dass nicht 1 fehlerhaft implementierter View
(das ist das Ding, was für einen Request eine Response erarbeitet), alle anderen 100 Anfragen blockiert - await vergessen, oder so?
Wie stellt man sicher, daß Software keine Bugs hat? ;-) Ja, das kann passieren, zeigt sich meiner Erfahrung nach aber ziemlich schnell.
Eher ungünstig, wenn eine kleine neue unbedeutende Seite in den Untiefen einer komplexen Web-Applikation plötzlich hunderttausendfach am Tag genutzte Uraltseiten zerstört.
Es geht mir nicht um Bugfreiheit, sondern darum, dass der eine Benutzer nicht mit den Folgen der Abbarbeitung eines Requests eines anderen Benutzers sieht/merkt. Aus meiner Sicht lässt sich nur so ein halbwegs vernünftiges produktiv eingesetztes Produkt erweitern/pflegen ohne, dass uns die Kunden pausenlos aufs Dachsteigen.
Man könnte auch sagen, sie sind daran gewöhnt, dass es bei neuen Sachen noch im Getriebe knirscht, aber der Rest stabil weiterläuft.
Die Nachteile kooperativen Multitaskings bleiben bestehen, auch wenn diese totgeschwiegen werden. ;-)
Keine Ahnung, ob die totgeschwiegen werden. Ich bleibe da bei meinem bodenständigen "man sollte wissen, was man tut".
Was prinzipiell dasselbe ist. ;-) Aber ich lass das mal so stehen.
Vermutlich wird niemand eine GUI basierend auf asyncio entwickeln wollen.
Kannst du erläutern, wieso nicht?
Massiv paralleles IO für Batchprozesse ist jetzt in der Regel nicht soooo komplex und ich bezweifle, daß es eine ähnlich einfache Lösung gibt, die gleiche Performance auf Multicoremaschinen zu erreichen.
Ich kann dir hier nicht ganz folgen.
Aber die meisten Web-Anwendungen, die Geschäftslogiken abbilden, sind dann doch eher ziemlich kompliziert/komplex und müssen ständig erweitert werden, wo ich mir gut vorstellen kann, dass man sich mit kooperativen Multitasking ziemlich sicher ins Knie schießt; auch je größer das Team wird. Da fehlen mir die Sicherungsmaßnahmen, die man bei einem separaten Prozess einfach hat.
Natürlich. Aber warum sollte ich für sowas asyncio verwenden wollen?
Vielleicht verstehe ich dich hier falsch, aber wenn es eine Anwendung gibt, die mit asyncio geschrieben wurde, dann ist die Entscheidung (möglicherweise basierend auf der 100K Anforderung) bereits durch.
Dennoch wirft man doch nicht einfach alle möglichen Engineering-Maßnahmen/Richtlinien aus dem Fenster, nur weil man sich für Tool A und nicht für Tool B entschieden hat.
Wie sehe die Lösung also aus? Mir fehlt da bisschen der Ansatz außer vielleicht am Ende doch wieder Prozesse zu verwenden, in denen dann eine asyncio-Loop für 20K läuft.
Aber bis dahin ist mein Verständnis, daß das, was du gerne hättest, ganz einfach nicht geht.
Schade. :-(
Deswegen oben mein Verweis auf "power feature". Der durchschnittliche Entwickler wird nie einen Grund haben, asyncio zu verwenden. Viele werden es versuche, weil es möglich ist. Ähnlich wie bei meta classes. Daß ein Feature mißbraucht wird, macht es aber nicht per se schlecht.
Das kann ich nur so unterschreiben.
Daher auch mein Nachgefrage, ob und wann es denn sinnvoll ist asyncio einzusetzen.
Damit kann ich leben. ;-) Gerne höre ich mir auf der nächsten Europython deinen Vortrag darüber an, wie man's besser machen könnte. Das hätte quasi schon Tradition. :-)
Hehe, wenn ich's wüsste, würd ich hier nicht diskutieren. ;-)
Habe dort noch nie einen Vortrag gehalten.
Hast du dir mal Lösungen in den anderen Web/Script-Sprachen angeschaut? Ruby, Php, ... Hast du da irgendwas gesehen, daß eleganter wäre, also das asyncio von Python?
Bei Ruby und PHP bin ich, ehrlich gesagt, komplett raus aus der Nummer (aber aus anderen Gründen, die mit dem Sprachdesign und der Philosophie zu tun haben).
Auch, wenn mich hier jetzt dafür wahrscheinlich einige steinigen, aber ich persönlich finde die Events und Callbacks von JavaScript gar nicht so schlecht. Und am Ende nimmt man halt wieder eine Bibliothek, die einem das wegkapselt, was unangenehm ist, wie AngularJS.
Sven

On 2018-01-18 23:50, Achim Domma wrote:
On Thursday, 18 January, 2018 11:11 PM, Sven R. Kunze wrote: Vielleicht ist das ein netter Blickwinkel: Für mich ist asyncio ein "power feature" für sehr spezielle Anwendungsfälle. Wenn ich die habe, nutze ich es, ansonsten nicht. Wenn's jemand aus Selbstzwecke oder Unwissenheit nutzt, ist das nicht mein Problem. [...] Deswegen oben mein Verweis auf "power feature". Der durchschnittliche Entwickler wird nie einen Grund haben, asyncio zu verwenden. Viele werden es versuche, weil es möglich ist. Ähnlich wie bei meta classes.
Das gibt ziemlich gut meinen Eindruck wieder: das asyncio-Framework als Hilfsmittel bei speziellen Anforderungen. Ich vermute, das wird auch in Zukunft nicht massenhaft eingesetzt werden. Nicht, weil es schlecht ist, sondern weil es im allgemeinen nicht nötig ist.
Viele Grüße Stefan

Sven R. Kunze schrieb am 18.01.2018 um 20:02:
On 17.01.2018 22:49, Stefan Behnel wrote:
Und anstatt meinen Code speziell für asyncio zu schreiben, ist es dann besser, ihn speziell für select() zu schreiben? Der "Vorteil" erschließt sich mir absolut nicht. Bei asyncio brauche ich den meisten Code *gar nicht* zu schreiben, weil es für fast alles schon fertige Tools und Bibliotheken gibt, die ich dank der allgemeinen Schnittstelle einfach ineinander stöpseln kann.
Sprichst du aus eigener Erfahrung?
Ja. Aber interessant, dass du es für nötig erachtest, explizit nachzufragen.
Jemand anderes im Thread hatte nach genau solcher Erfahrung gefragt. Für mich liest sich das, was du da schreibst, leider immer noch nach einer Wiederholung des Marketings.
Was ja im eklatanten Gegensatz zu deinen eigenen Kommentaren steht. ;)
Den asyncio-Quellcode, den ich bisher immer zu Gesicht bekommen habe, involvierte ab einer bestimmten Komplexitätsgrenze stets Threads. Wo ich mich dann naturgemäßg wieder frage, ob ich da im falschen Film bin, wo doch gerne so über Threads geschimpft wird.
Threads können genau zwei Dinge gut: 1) parallele Berechnung unabhängiger Daten, also trivial-parallelen Code ausführen, und 2) nichts tun, z.B. indem sie (im Rahmen vorhandener Ressourcen) auf I/O warten. Alles andere führt ziemlich schnell zu Komplexitätsexplosion und sinkender Effizienz.
Daraus folgt: es ist nichts dagegen einzuwenden, Threads sinnvoll einzusetzen, nämlich genau in den Bereichen, die sie gut abdecken.
Insbesondere bieten Thread-Pools eine gute (und simple) Möglichkeit, synchrone Tools in die async-Welt zu integrieren, z.B. synchrone Datenbanktreiber und SQLAlchemy, weil die Threads sich dort eben genau auf's Nichtstun und unabhängige Datenverarbeitung beschränken lassen.
Das Ineinander-Stöpseln, was ich da erlebt habe, war dann mit so einer riesigen Menge Boilerplate verbunden, dass ich diese Implementierung nicht wirklich ernstnehmen konnte. Für mich war es eher nur Show-Case anstelle einer Implementierung mit Mehrwert. Da wäre ich jetzt wirklich interessiert an realem Beispielen.
Um beim Beispiel zu bleiben, SQLAlchemy lässt sich mit ca. 10-20 Zeilen leicht lesbarem Code mit Hilfe eines Thread-Pools in async-Frameworks integrieren. Dann rufst du statt "query.all()" eben "await background_query(query.all)" auf, und schon blockiert's nicht mehr und erlaubt gleichzeitig 'unbegrenzten' I/O-Durchsatz. Empfinde ich jetzt noch nicht als "riesige Menge Boilerplate", und funktioniert auch mit etlichen anderen thread-sicheren synchronen APIs.
Dennoch, die Problempunkte, die ich und viele andere immer wieder anbringen, werden auch hier nicht wirklich entkräftet und du gehst nicht ansatzweise darauf.
Na ja, selektiver Wahrnehmung ist eben schwer mit Argumenten zu begegnen. Wenn es deine Meinung ist, dass asyncio der Teufel ist und alle anderen schlicht nicht genug Erfahrung haben und im Zweifel nur noch nicht die richtigen (furchtbaren) Code-Beispiele gesehen haben, die dich doch längst in deiner Meinung verfestigt haben, dann helfen Argumente nur bedingt.
Klingt jetzt nach Ad-Hominem, ist mir klar, aber eigentlich beschreibt es nur die Art, wie deine "Argumentation" in diesem Thread auf mich wirkt.
Solange wir nur auf dem Niveau "meins ist besser" argumentieren, können wir hier auch aufhören. Anderenfalls würde ich gerne hören, wie sich die asyncio-Gemeinde die Lösung des Zwei-Welten-Problems vorstellt. Zum Beispiel anhand von WSGI.
Du solltest die Möglichkeit in Erwägung ziehen, dass das vielleicht einfach gar kein "Problem" ist, das eine "Lösung" erfordert. Das Neuschreiben von Tools für async ist eigentlich nie ein Zwang, sondern entweder eine Optimierung bestehender (sync-)Möglichkeiten, oder ein "cooles Projekt in der Freizeit", oder irgendwas anderes, was Leute eben machen *wollen*.
So ist das halt bei OpenSource, niemand hält einen davon ab, das 5254. Template-System zu schreiben, oder die 22. Neuimplementierung von Python, "weil's ja so einfach ist" und "ich das viel besser kann als alle anderen", oder weil ich vielleicht einfach etwas dabei lernen möchte. Genauso hält einen niemand davon ab, ein cooles neues async-Tool zu schreiben, obwohl es das alles "ja schon längst gibt". Leb einfach damit, dass andere Leute Dinge machen, weil sie sie machen wollen, und nicht, weil du der Meinung bist, dass sie sinnvoll sind. Andere Menschen haben andere Anforderungen und andere Vorstellungen davon, was "sinnvoll" oder "cool" ist. Es gibt auch genug Leute, die aus voller Überzeugung Java verwenden, weil's "cool" ist. Kann man so oder so dazu stehen.
Stefan

On 2018-01-19 11:17, Stefan Behnel wrote:
Sven R. Kunze schrieb am 18.01.2018 um 20:02:
On 17.01.2018 22:49, Stefan Behnel wrote: Das Ineinander-Stöpseln, was ich da erlebt habe, war dann mit so einer riesigen Menge Boilerplate verbunden, dass ich diese Implementierung nicht wirklich ernstnehmen konnte. Für mich war es eher nur Show-Case anstelle einer Implementierung mit Mehrwert. Da wäre ich jetzt wirklich interessiert an realem Beispielen.
Um beim Beispiel zu bleiben, SQLAlchemy lässt sich mit ca. 10-20 Zeilen leicht lesbarem Code mit Hilfe eines Thread-Pools in async-Frameworks integrieren. Dann rufst du statt "query.all()" eben "await background_query(query.all)" auf, und schon blockiert's nicht mehr und erlaubt gleichzeitig 'unbegrenzten' I/O-Durchsatz. Empfinde ich jetzt noch nicht als "riesige Menge Boilerplate", und funktioniert auch mit etlichen anderen thread-sicheren synchronen APIs.
Mich würde es sehr interessieren, diesen "leicht lesbaren" Code zu sehen. :-) Ist der auch leicht lesbar für jemanden, der mit asyncio noch relativ wenig Erfahrungen hat?
Wie schwierig ist es unter diesen Umständen, selber auf diesen Code zu kommen?
Ein weiterer Punkt: Ist das generischer Code, der sich auch auf andere synchrone APIs anwenden lässt oder ist es überwiegend Code, der speziell auf SQLAlchemy zugeschnitten ist?
Solange wir nur auf dem Niveau "meins ist besser" argumentieren, können wir hier auch aufhören. Anderenfalls würde ich gerne hören, wie sich die asyncio-Gemeinde die Lösung des Zwei-Welten-Problems vorstellt. Zum Beispiel anhand von WSGI.
Du solltest die Möglichkeit in Erwägung ziehen, dass das vielleicht einfach gar kein "Problem" ist, das eine "Lösung" erfordert.
Das habe ich zwar auch schon überlegt, dass es so sein könnte, aber bisher habe ich nichts konkretes gesehen, was mich davon überzeugen würde.
Das Neuschreiben von Tools für async ist eigentlich nie ein Zwang, sondern entweder eine Optimierung bestehender (sync-)Möglichkeiten, oder ein "cooles Projekt in der Freizeit", oder irgendwas anderes, was Leute eben machen *wollen*.
So ist das halt bei OpenSource, niemand hält einen davon ab, das 5254. Template-System zu schreiben, oder die 22. Neuimplementierung von Python, "weil's ja so einfach ist" und "ich das viel besser kann als alle anderen", oder weil ich vielleicht einfach etwas dabei lernen möchte. Genauso hält einen niemand davon ab, ein cooles neues async-Tool zu schreiben, obwohl es das alles "ja schon längst gibt". Leb einfach damit, dass andere Leute Dinge machen, weil sie sie machen wollen, und nicht, weil du der Meinung bist, dass sie sinnvoll sind. Andere Menschen haben andere Anforderungen und andere Vorstellungen davon, was "sinnvoll" oder "cool" ist. Es gibt auch genug Leute, die aus voller Überzeugung Java verwenden, weil's "cool" ist. Kann man so oder so dazu stehen.
Ich habe ja überhaupt kein Problem damit, wenn Leute etwas aus Interesse entwickeln. Ich finde es nur unschön, wenn man diese zusätzlichen (gegenüber den synchronen Libraries) entwickeln _muss_, um sie im asyncio (im Sinne von `async`/`await`) nutzen zu können.
Ein paar Gedanken zur Vermittlung von Asyncio in Python:
Die meisten Artikel, die ich dazu bisher gesehen habe, sind Einführungen anhand relativ einfacher Beispiele. Um den Leser nicht gleich zu "erschrecken", werden Aufgabe und Lösung so gewählt, dass sie gut ins Asyncio-Paradigma bzw. den Support dafür in Python passen.
Aber wie geht es weiter, wenn man sich nicht nur einen Überblick verschaffen, sondern Pythons Asyncio in "richtigen" Projekten einsetzen will? Da habe ich bisher extrem wenig Dokumentation zu gefunden. Ich glaube, das hier geht in die richtige Richtung: https://pymotw.com/3/asyncio/index.html .
Auf der anderen "Seite" von Einsteiger-Tutorials finden sich die technisch ausführliche Dokumentation wie die unter https://docs.python.org/3/library/asyncio.html . Da mag zwar alles drin stehen, aber als Einsteiger sieht man den Wald vor lauter Bäumen nicht. Das ist, wie eine natürliche Sprache aus einem Wörterbuch und einer Grammatik-Referenz lernen zu wollen.
Kennt ihr weitere Webseiten und gegebenenfalls Bücher, die den Einsatz von Python-Asyncio in nicht-trivialen Projekten erläutern?
Stefan, ich hatte auch schon überlegt, dir vorzuschlagen, dazu einen Vortrag auf der PyCon DE zu halten. Ich fürchte aber, dass ein Vortrag nicht das richtige Medium dafür ist bzw. in der verfügbaren Zeit kaum über eine Asyncio-Einführung hinauskommt. Was meinst du?
Viele Grüße Stefan

,Stefan Schwarzer schrieb am 20.01.2018 um 00:02:
On 2018-01-19 11:17, Stefan Behnel wrote:
Sven R. Kunze schrieb am 18.01.2018 um 20:02:
On 17.01.2018 22:49, Stefan Behnel wrote: Das Ineinander-Stöpseln, was ich da erlebt habe, war dann mit so einer riesigen Menge Boilerplate verbunden, dass ich diese Implementierung nicht wirklich ernstnehmen konnte. Für mich war es eher nur Show-Case anstelle einer Implementierung mit Mehrwert. Da wäre ich jetzt wirklich interessiert an realem Beispielen.
Um beim Beispiel zu bleiben, SQLAlchemy lässt sich mit ca. 10-20 Zeilen leicht lesbarem Code mit Hilfe eines Thread-Pools in async-Frameworks integrieren. Dann rufst du statt "query.all()" eben "await background_query(query.all)" auf, und schon blockiert's nicht mehr und erlaubt gleichzeitig 'unbegrenzten' I/O-Durchsatz. Empfinde ich jetzt noch nicht als "riesige Menge Boilerplate", und funktioniert auch mit etlichen anderen thread-sicheren synchronen APIs.
Mich würde es sehr interessieren, diesen "leicht lesbaren" Code zu sehen. :-) Ist der auch leicht lesbar für jemanden, der mit asyncio noch relativ wenig Erfahrungen hat?
Wie schwierig ist es unter diesen Umständen, selber auf diesen Code zu kommen?
Absolut, hier ist die triviale Minimalversion:
""" size_of_connection_pool = config.get(...)
pool = ThreadPoolExecutor(max_workers=size_of_connection_pool)
def background_query(func, *args, **kwargs): return pool.submit(func, *args, **kwargs) """
Der Produktivcode ist dann doch noch ein bisschen komplexer, eben eher so 20 Zeilen, aber da ist schon wieder einiges Projektspezifische drin. Eine eigene Funktion (statt direkt den Pool) dafür zu nehmen hat den Vorteil, dass es a) die komplette Funktionalität hinter einem hübschen Namen kapselt, und b) der explizite Funktionsname es den Entwicklern schwerer macht, zu sagen: "och, da ist eh schon ein Thread-Pool, dann kann ich den ja auch noch für ein paar andere Dinge nehmen". Genau das sollte nämlich nicht getan werden, um das Ganze kontrollierbar zu halten.
Übrigens ist die DB-Anbindung hier der optimale Fall, weil der Connection-Pool ja ohnehin eine feste Maximalgröße hat. Da können dann nochmal genau so viele Threads drauf warten, und fertig.
Ein weiterer Punkt: Ist das generischer Code, der sich auch auf andere synchrone APIs anwenden lässt oder ist es überwiegend Code, der speziell auf SQLAlchemy zugeschnitten ist?
Das ist genau der Grund, weshalb ich sowas nicht in eine Bibliothek stecken würde, sondern entweder direkt im Projekt oder in einem Commons-Modul habe. Oft lohnt es sich, die Funktionalität in der Wrapperfunktion ein bisschen auszuweiten. Findet sich eigentlich immer was, sei es spezifisches Logging, Fehlerbehandlungen, oder was auch immer. Dann wird die "background_query" Funktion eben selbst zu einer async-def Koroutine und loggt nach dem Aufruf z.B. noch die Ausführungsdauer raus.
Ein paar Gedanken zur Vermittlung von Asyncio in Python:
Die meisten Artikel, die ich dazu bisher gesehen habe, sind Einführungen anhand relativ einfacher Beispiele. Um den Leser nicht gleich zu "erschrecken", werden Aufgabe und Lösung so gewählt, dass sie gut ins Asyncio-Paradigma bzw. den Support dafür in Python passen.
Aber wie geht es weiter, wenn man sich nicht nur einen Überblick verschaffen, sondern Pythons Asyncio in "richtigen" Projekten einsetzen will? Da habe ich bisher extrem wenig Dokumentation zu gefunden. Ich glaube, das hier geht in die richtige Richtung: https://pymotw.com/3/asyncio/index.html .
Na ja, in den seltensten Fällen wirst du asyncio direkt verwenden. Für alles Web-basierte (Webseiten oder REST-APIS) gibt es z.B. Tornado, aiohttp oder Twisted-Web als Frameworks (Tornado und Twisted sind übrigens auch schon deutlich älter als asyncio, können es aber inzwischen verwenden).
Auf der anderen "Seite" von Einsteiger-Tutorials finden sich die technisch ausführliche Dokumentation wie die unter https://docs.python.org/3/library/asyncio.html . Da mag zwar alles drin stehen, aber als Einsteiger sieht man den Wald vor lauter Bäumen nicht. Das ist, wie eine natürliche Sprache aus einem Wörterbuch und einer Grammatik-Referenz lernen zu wollen.
Das Gute ist: die Doku kannst du als Normalnutzer fast komplett ignorieren. Betrachte asyncio am besten als Infrastrukturbaustein. So, wie es mit dem ThreadPoolExecutor eine hübsche API für das threading-Modul gibt, gibt es auch entsprechende Frameworks und Tools, die auf asyncio aufsetzen. Vor allem Twisted ist ja ein großer Baukasten mit allen möglichen Netzwerkprotokollen, bis hin zu SMTP und SSH, und Tornado und aiohttp sind schicke Web-Frameworks.
Das Gute an asyncio und async/await gegenüber der Zeit davor, ist, dass all diese Frameworks nun kompatibel werden, und viel async-Code sich über Framework-Grenzen hinweg wiederverwenden lässt. Übrigens ganz ähnlich wie bei Generatoren in Python. Damit lässt sich auch vieles generisch und wiederverwendbar implementieren.
Stefan, ich hatte auch schon überlegt, dir vorzuschlagen, dazu einen Vortrag auf der PyCon DE zu halten. Ich fürchte aber, dass ein Vortrag nicht das richtige Medium dafür ist bzw. in der verfügbaren Zeit kaum über eine Asyncio-Einführung hinauskommt. Was meinst du?
Na ja, das Problem ist so ein bisschen: es gibt da ja eigentlich nur wenig Neues. Ja, es funktioniert jetzt so langsam alles mit allem, aber das, was da funktioniert, das funktioniert eigentlich auch schon länger. Wo also ist da das interessante Thema für einen Vortag, der den ZuhörerInnen inhaltlich auch irgendwas bringt? Asyncio ist da halt die falsche Ebene, und zwar die völlig falsche.
Die meisten async-Anwendungen, an denen ich gearbeitet habe, verwenden Tornado, ein paar statt dessen Twisted oder aiohttp. Dazu verschiedene Datenbanken und andere Backends, externe Services, Queues oder was weiß ich was. Der I/O-Loop selbst ist auf der Ebene völlig irrelevant, der muss nur einfach da sein und funktionieren. Und der Anwendungscode sieht dann auch nicht wesentlich anders aus als anderswo auch, nur eben mit async/await oder Py2-Koroutinen statt "normalen" Funktionen.
Das ist ja gerade der Vorteil von async/await, dass sich eigentlich nichts an der Programmierung ändert. Nur Kommunikation und Delegation wird eben mit "await" (oder "yield" in Py2) explizit sichtbar im Code. Und die komplette Anwendung läuft weiterhin single-threaded, vermeidet also Race-Conditions und die ganzen Multi-Threading-Fußfallen, die auftreten, sobald ich (aus Effizienzgründen) irgendwelchen Zustand global halten muss.
Stefan

On 20.01.2018 13:37, Stefan Behnel wrote:
Stefan Schwarzer schrieb am 20.01.2018 um 00:02:
On 2018-01-19 11:17, Stefan Behnel wrote:
Dann rufst du statt "query.all()" eben "await background_query(query.all)" auf, und schon blockiert's nicht mehr und erlaubt gleichzeitig 'unbegrenzten' I/O-Durchsatz. er
Absolut, hier ist die triviale Minimalversion:
""" size_of_connection_pool = config.get(...)
pool = ThreadPoolExecutor(max_workers=size_of_connection_pool)
def background_query(func, *args, **kwargs): return pool.submit(func, *args, **kwargs) """
Da wir nun endlich mal Quelltext haben, sieht die Welt schon etwas verständlicher aus.
Um das jetzt zusammenzufassen:
1) es gibt einen Steuerprozess, der muss async sein und in dem läuft auch die Loop 2) blockierender Code ist inkompatibel, da nicht kooperativ, und wird in ThreadPools ausgelagert, wo er keinen Schaden anrichten kann
Stimmt das soweit?
Mit anderen Worten, wenn ich in den Projekten bereits einen Aufgaben-Verteiler-Prozess (Steuerprozess) habe, dann brauche ich mir um asyncio eigentlich keine Gedanken zu machen. Die Schleife hat schon jemand anderes programmiert, mein Zeug wird ausgelagert in Threads oder Prozesse, da diese nicht kooperativ sind (z.B. Apache WSGI).
Also braucht nur derjenige asyncio, der auch diese Schleifen programmiert, um seine Schleife durch die EventLoop von asyncio eventuell abzulösen.
Hatte ich vor asyncio keine Schleife, brauche ich jetzt auch kein asyncio, weil es keine Schleife abzulösen gibt.
Und der Trick mit dem "unbegrenztem IO-Durchsatz" kommt durch den fehlenden Overhead mehrerer Prozesse/Threads, welcher in einer einfacher Liste im Steuerprozess effizienter abgebildet werden kann (durch die Queue im PoolExecutor).
Sven

Sven R. Kunze schrieb am 22.01.2018 um 17:14:
- es gibt einen Steuerprozess, der muss async sein und in dem läuft auch
die Loop 2) blockierender Code ist inkompatibel, da nicht kooperativ, und wird in ThreadPools ausgelagert, wo er keinen Schaden anrichten kann
Stimmt das soweit?
Etwas vereinfacht und in Punkt 2) fehlt noch ein "beispielsweise", aber ja, kann mensch so sagen.
Mit anderen Worten, wenn ich in den Projekten bereits einen Aufgaben-Verteiler-Prozess (Steuerprozess) habe, dann brauche ich mir um asyncio eigentlich keine Gedanken zu machen.
Doch, denn genau das war ja der Aufmacher für diese Diskussion. Es lohnt sich oft, einen wie auch immer existierenden (async-)Steuerprozess durch asyncio zu ersetzen, weil dadurch ein ganzer Haufen wiederverwendbarer Code verfügbar wird.
Du hast von einem sync/async Zwei-Welten-Problem gesprochen, aber das wirkliche Problem war die Zersplitterung innerhalb der async-Welt. Das ist das, was asyncio beseitigt.
Stefan

Am 17.01.2018 um 19:36 schrieb Sven R. Kunze srkunze@mail.de:
Da verstehe ich nicht, warum nicht proaktiv auf dieses Problem eingegangen wird. Kein normaler Programmierer will alle seine Bibliotheken auf eine zweite Programmiersprache umschreiben. Das ist das Zwei-Welten-Problem bei asyncio. Stattdessen höre ich immer nur: "du machst das falsch", "asyncio ist die Zukunft" etc.
Mal ein anderer Gedanke dazu. Was wäre wenn die allermeisten Programmierer bis zu dem Zeitpunkt als das Internet im öffentlich/kommerziellen Bewusstsein angekommen war, also ca. 1995, ein ziemlich „isoliertes“ Verständnis ihrer Zunft (oder auch Kunst) hatten, einfach, weil ihr täglich Brot so simpel war, dass sie es immer mit schönen, seriellen Algorithmen auf SISD-Rechnern (single instruction single data) zu tun hatten?
Seitdem gibt es langsam fast nur noch Multi-Core-Rechner, die in einer vernetzten Welt miteinander interagieren. In dieser hat man es mit Problemen zu tun, die es vorher einfach nicht oder nur für sehr spezialisierte Anwendungen/Entwickler gab. Heute ist „verteiltes“ Rechnen nicht mehr eine Sache für Wissenschaftler, sondern immer mehr täglich Brot für ganz normale Anwendungen und Firmen. Wo früher vielleicht eine MPI-Bibliothek benutzt worden wäre, verwendet man heute vielleicht Spark/Hadoop/Flink etc. mit entsprechenden Programmiersprachen dazu, die sich leicht(er) parallelisieren lassen, also funktionale wie z.B. Scala.
Auf höherer Ebene gibt es Lösungen wie ZeroMQ u.a. von Peter Hintjens, der sehr eindrucksvoll über die unausweichlich zunehmende Bedeutung dezentraler Anwendungen geschrieben hat. Sinngemäß lautet eine Aussage von ihm, dass wir lernen müssen, solche Anwendungen zu schreiben, einfach, weil es kaum noch andere mehr geben wird. Und in solchen Anwendungen sind auch einige vermeintlich triviale Dinge wie I/O eben nicht mehr so trivial. Ein schöner Artikel dazu ist dieser hier, der auch auf asyncio eingeht:
https://vorpus.org/blog/timeouts-and-cancellation-for-humans/
Natürlich gibt es immer mehrere Ansätze für alles, aber irgendwann konvergiert auch so einiges wieder, und ich finde es wenig hilfreich, wenn man die Methode, die man selbst gewohnt ist, als die allein glücklich machende für alle propagiert, oder dafür Anekdoten von „alten Männern“ bemüht. (Wenn man sich dazu zählt, darf man das sagen. :-) Ich könnte auch behaupte, ich habe Kollegen, die als Software-Architekten (Java/Scala) arbeiten und die mir sagen, „I’m too stupid for OOP“. Was sagt uns das, nicht viel. Mache ich also lieber nicht, auch wenn es stimmt.
Python hat mit asyncio zumindest eine sehr aussichtsreiche Lösung direkt in der Sprache eingebaut. Die Verbreitung nimmt mit Sicherheit eher zu als ab. Und ich zumindest würde mich freuen, wenn es Python damit besser gelänge, auch in einem ganz bestimmten Bereich mit anderen Sprachen mitzuhalten, die von vorn herein dafür entwickelt wurden.
Beste Grüße,
Dinu

On 17.01.2018 23:06, Dinu Gherman wrote:
[Marketing-Sprech für asyncio]
Die Motivation für asyncio is hinglänglich bekannt. Das hilft doch aber nicht bei den konkreten Problemen, die ich da aufgeführt habe und viele fühlen sich mit diesen Problemen allein gelassen.
https://vorpus.org/blog/timeouts-and-cancellation-for-humans/
Twitter war hier schneller als die Mailingliste. ;-) Sehr guter Artikel. Sehr empfehlenswert und konzeptionell gut durchdacht mit Beispielen, die auch einen praxisrelevanten Mehrwert erkennen lassen. Trotzdem nicht wirklich des Rätsels Lösung, wenn man bedenkt, dass man das für jeden kleinen Firlefanz machen müsste.
Darüber hinaus, kann man doch eh nie sagen, ob eine x-beliebige Funktion I/O machen würde theoretisch. Das war ein weiterer Kritikpunkt: "warum nicht gleich alles im asyncio-Modus laufen lassen?" Die Proliferation von async und await halte ich für durchaus problematisch, falls sich es durchsetzen sollte.
Natürlich gibt es immer mehrere Ansätze für alles, aber irgendwann konvergiert auch so einiges wieder, und ich finde es wenig hilfreich, wenn man die Methode, die man selbst gewohnt ist, als die allein glücklich machende für alle propagiert, oder dafür Anekdoten von „alten Männern“ bemüht. (Wenn man sich dazu zählt, darf man das sagen. :-) Ich könnte auch behaupte, ich habe Kollegen, die als Software-Architekten (Java/Scala) arbeiten und die mir sagen, „I’m too stupid for OOP“. Was sagt uns das, nicht viel. Mache ich also lieber nicht, auch wenn es stimmt.
Python hat mit asyncio zumindest eine sehr aussichtsreiche Lösung direkt in der Sprache eingebaut. Die Verbreitung nimmt mit Sicherheit eher zu als ab. Und ich zumindest würde mich freuen, wenn es Python damit besser gelänge, auch in einem ganz bestimmten Bereich mit anderen Sprachen mitzuhalten, die von vorn herein dafür entwickelt wurden.
Ich kann das sehr gut nachvollziehen. Ich persönlich hätte mich allerdings mehr über die Umsetzung, wie sie golang geschehen ist, gefreut.
Sven

Am 16.01.2018 um 19:55 schrieb Stefan Behnel:
Sven R. Kunze schrieb am 15.01.2018 um 20:55:
On 15.01.2018 17:07, Diez B. Roggisch wrote:
On 15. Jan 2018, at 16:46, Thomas Güttler wrote: Was ist aus deiner Sicht das, was alle nehmen?
Ich rate mal: asyncio. Die Chance, das du zB http-clients da einfach einpluggen kannst, und damit schon auf einem deutlich besseren Abstraktionsniveau unterwegs bist, als mit rohen Filedeskriptoren und epoll zu arbeiten laesst mich vermuten, dass es sich dabei um das Alufelgenrad handelt.
Aus meiner Sicht gequilter Unsinn. asyncio is ein Nischenprodukt und alle Welt verwendet File-Deskriptoren. Wunschdenken sollte nicht mit der Realität verwechseln werden.
Sockets sind meiner Meinung nach ein viel besseres Abstraktionsniveau als meinen Quellcode überall mit diesem dämlichen async und await zu spicken.
Ich verstehe nicht, warum das ein Argument dafür sein sollte, sich seinen eigenen I/O-Loop zu schreiben. AsyncIO wurde exakt und einzig und allein aus dem Grund geschrieben, dass es bereits zu viele davon gab, die alle nicht miteinander kompatibel waren. Wenn Thomas also jetzt darüber nachdenkt, sein handgedrechseltes select() noch weiter zu einem solchen auszubauen, dann nehme ich das als Anreiz, auf die vergleichbaren Fehler anderer hinzuweisen, aus denen bereits nachhaltig Lehren gezogen wurden.
Hallo,
danke für den Hinweis. Ich hatte bloß mit "voll erweiterbare, luftgepolsterte Alufelgenrad" überhaupt nichts anfangen können. Mit obigen Zeilen von dir aber schon.
Ein hübsches Ergebnis ist jetzt z.B., dass das Tornado Web-Framework in der Version 5 komplett auf asyncio umziehen wird und in der Folgeversion den eigenen I/O-Loop über den Jordan jagt. Das große Aufräumen hat begonnen.
Und eine zweite Implementierung von asyncio gibt es mit uvloop auch schon, so dass jetzt auf beiden Seiten der Schnittstelle die freie Auswahl besteht. AsyncIO ist auf dem besten Weg, für die Welt des kooperativen Multitaskings so etwas zu werden wie WSGI es seit Jahren schon für synchrone Web-Frameworks ist.
... Ja, ich wünschte ich könnte Python 2.7 schnell hinter mir lassen. Das geht aber nicht. Halt, das ist aber ein neuer Thread. Der hier ist schon lang genug.
Gruß, Thomas
participants (8)
-
Achim Domma
-
Diez B. Roggisch
-
Dinu Gherman
-
Stefan Behnel
-
Stefan Schwarzer
-
Sven R. Kunze
-
Thomas Güttler
-
Thomas Güttler Lists