Transkript: Python Async Teil 1 - am Beispiel von Django
Vollständiges Transkript der Episode. Zeitstempel beziehen sich auf die Audiowiedergabe.
Ja, hallo liebe Hörerinnen und Hörer, willkommen beim Python Podcast.
Heute ist die 23.
Episode, also für uns wieder eine ganz besondere Zahl.
Was machen wir heute?
Heute machen wir wieder Async und Multisweading und sowas.
Und zwar Django Async kommt bald raus und darüber haben wir eine eigene Folge aufgenommen.
Und zwar in einem schönen Wintergarten von Jochen.
Ich bin der Dominik.
Und diesmal haben wir natürlich wieder einen Gast.
Den kennt ihr vielleicht schon.
Hallo Johannes.
Hallo Dominik.
Hallo Jochen.
Und hallo Jochen.
Hallo, genau, auch wieder dabei.
Ja, falls euch irgendwas gefällt oder nicht gefällt, dann schreibt uns doch wie immer gerne eine E-Mail an.
Hallo.python.gasse.
Und wir möchten uns auch gerne noch für ein paar Abonnenten bedanken.
Zwar haben wir allein auf Spotify mittlerweile über 1111 Abonnenten.
Herzlichen Dank dafür und wuhuu!
Sehr cool, wir freuen uns.
Wir finden ja toll.
Naja.
Mir reicht es noch nicht, du wirst die Welt verstanden.
Du guckst zu viele YouTube-Channels an.
Ich weiß nicht genau.
Ist das da so üblich?
Ich weiß nicht, ob das beim Podcast auch so üblich ist.
Ach was denn?
Ich habe mich einfach gefreut und ich finde das gut.
Ja, nee, ist schon toll, aber keine Ahnung.
Ja, und ja, wo wir gerade schon bei so anderen Sachen sind, also ihr habt ja vielleicht den Thomas gehört, der mir uns eine schöne Folge gemacht hat und der hat jetzt seinen Podcast umbenannt.
Also wir hatten ihn das letzte Mal zu wenig erwähnt, deswegen müssen wir es auch einmal extra erwähnen.
Der ist ein ziemlich cooler Podcast, den er da hat.
Wie heißt der jetzt, Jochen?
Death's Stats.
Der hat Kevin Sisson dabei in einem letzten Talk und redet über Pi MC 3.
Ich glaube, es ist sehr hörenswert, die Co-Founder von
der gestern schon gehört, fand es sehr gut.
Also, ja.
Ja, vielen Dank, komm mal für die nächste tolle Episode.
Ja, und hätte ich jetzt gar nicht gedacht, ich meine, dass das Instagram Python macht, aber dass sich halt deren Gründer und Ex-CEO irgendwie so viel mit Statistik und Machine Learning-Kram so beschäftigt ist und Data Science.
Oder naja gut, das hat er ja vorher nicht so gemacht, aber das macht er jetzt halt, das fand ich schon sehr interessant und war auf jeden Fall eine interessante Geschichte.
Ja, hört doch da mal rein, wenn ihr möchtet.
Ja, wir machen heute wieder Django, vor allen Dingen halt, weil Django 3.1 jetzt im August erscheint und tatsächlich jetzt mehr Async Features unterstützt als in der 3.0-Version, die seit Dezember draußen ist.
Und wir möchten ein bisschen mehr mit euch darüber reden oder untereinander darüber reden, was das überhaupt heißt, was das alles so ist, was ist überhaupt dieses Multispading.
Wir hatten das in einer Episode schon mal so ein bisschen angerissen, vielleicht auch so ein bisschen kurz gesprochen, aber uns sind noch so ein paar andere Erkenntnisse gekommen und darüber wollten wir halt so sprechen, warum macht man so ein Swett und warum macht man so einen Multiprozess und warum macht man das dann irgendwie Async und warum machen wir das mit Async.io und was gibt es dann noch für Alternativen?
Wann braucht man das überhaupt und warum und warum vielleicht auch überhaupt nicht?
Und irgendwie so, ne?
Ja, wollten
wir vielleicht erst noch so ein bisschen News machen oder so,
keine Ahnung?
Oh, News, ja, du hast natürlich recht.
Neuigkeiten aus der Szene.
Ja, wobei, so wahnsinnig viel gab es nicht.
Ich habe mir nur so ein paar Dinge aufgeschrieben.
Ich weiß nicht, ob du das verwendest, insofern, fänst du vielleicht da wirklich.
Pylance.
Pylance.
Ach, ja,
das ist das.
Ja, sehr gut.
Ja, also Pylend ist ein neues Plugin, was für VS Code ausgekommen ist.
Also ein neuer Language-Server, wenn ich das richtig verstehe.
Der ziemlich coole Sachen macht.
Also mit der Auto-Imports jetzt in VS Code und so und fast alle Features, die man sonst so auf Pycharm eher hatte.
Und also ich mag ihn sehr gerne.
Hast du ihn auch schon aktiv im Einsatz?
Ich habe das auch schon aktiviert und finde es auch ziemlich gut.
Also ich mache ja jetzt gar nicht so viel mit Type-Annotationen, aber die Verlockung wird stärker.
Ja, ja.
Und das Ding kann da irgendwie einiges rausholen, ja.
Tja, Johannes, da bist du raus, wenn du nutzt Pycharm, ne?
Ja, tut mir leid, ich hab nicht das alles schon.
Ne, ich meine, ich benutze ja auch PyCharm.
Also insofern, also ich finde das auch gar nicht schlecht.
Du wurdest dazu gezwungen, habe ich gehört, Jochen.
Naja, mehr oder weniger.
Aber ja.
Ich meine, ich finde es ja auch interessant.
Also ab und zu mal andere Dinge verwenden es vielleicht gar nicht so schön.
Uns nutzen uns alle VS Code.
Achso, wer irgendwas auch VS-Code natürlich nutzt, ist Daniel Feldroy, falls ihr ihn kennt, der Autor von Tooth Schools of Django, der ja das bald das 3X-Version-Buch rausnimmt.
Und ich war letztens bei ihm ein, zweimal in seinem Twitch-Stream, weil er irgendwie, er streamt irgendwie Twitch und er hat mir versprochen, dass er in Düsseldorf zum Essen vorbeikommt.
Bin natürlich ganz gespannt, ob das irgendwie nicht nur so habe, dass wir gemacht haben.
Ja, das ist immer, das kann man leicht versprechen, solange keine Flugzeuge fliegen und so, ist das immer irgendwie relativ.
Oh, warst du nicht aus der aus Istanbul?
Ich so, nee, das war aus Düsseldorf, aber.
Das ist relativ gesehen nappisch.
Ach, Düsseldorf, Istanbul.
Kann man sich dann da Bücher signieren lassen?
Ja, also wenn die Alter gehen, auf jeden Fall.
Ja, das hat er ja auch gesagt an der Altheit.
Vielleicht verdiche ich dann sogar dahin und dann quatscht mir ein bisschen.
Nein, da fand ich ja super.
Wollte ich nur mal kurz erwähnen haben.
Ja, aber das stimmt.
Also ich gucke in letzter Zeit auch gerade in Vorbereitung auf diese Episode, habe ich relativ viele YouTube-Channels und so geguckt.
Und da verwenden doch sehr viele VS-Code.
Du hast dich vorbereitet.
Ja, ausnahmsweise mache ich ja sonst noch.
Zeichen und Wunder.
Ja, ich fand es auch sehr unangenehm, ehrlich gesagt.
Aber gerade zum Beispiel, da gab es eine Serie von Lukas Langer, der hat auch VS-Code verwendet.
Zu Async.io und so.
Ja, auch sehr empfehlenswert, kommt es auch noch in die Show-Nutz.
Ja, ansonsten war ich, glaube ich, irgendwie für 3.7 ist die letzte Bugfix-Release irgendwie gerade erschienen.
Für 3.6 noch irgendeine Security-Geschichte Python und das war es dann so ziemlich irgendwie mit.
Ja, dann bald ist 3.9 ist durch, ne?
Ja, bald kommt dann 3.9, genau.
Was ist denn das Tolle an 3.9?
Jetzt hast du jetzt noch auf dem falschen Fuß erwischt.
Also ich meine, das Tolle an 3.8 ist ja der Walrus-Operator und den wir auch alle ständig immer benutzen.
Ja.
Ich wüsste nicht, was in 3.9
Neues drin ist.
Doch, also ich habe gehört, dass zum Beispiel diese Subinterpreter-Geschichte da irgendwie mit drin sein soll schon.
Also das war fraglich eine Zeit lang und dann gab es irgendwie.
Bitte erkläre uns an, was ist ein Subinterpreter?
Ja, das ist im Grunde auch so eine Methode, um dann halt eventuell, also ja, um halt den Gill so ein bisschen loszuwerden oder halt das so ein bisschen.
Gill ist der Global Interpreter-Log, ja?
Ja, um das so.
Genau, die Konsequenzen so ein bisschen zu mitigieren.
Und das sollte.
Oder ich weiß es nicht, vielleicht auch was auch interessieren.
Ich habe keine Ahnung.
Also ich habe gehört, Python 3.9 unterstützt einen neuen Parser und der macht ganz viel anders und deswegen kann man jetzt auf einmal ganz viele andere Code-Analyse-Tools oder irgendwas benutzen.
Ja.
Aber diese Pack-Parser-Geschichte, ja.
Ja, weiß ich aber auch nicht so viel zu.
Also für den normalen Programmierer erstmal keine Unterschiede.
Ja, nicht so richtig viel.
Also muss sich mal mit beschäftigen.
Vielleicht gibt es auch irgendwelche total coole Sachen.
Na gut.
Ansonsten Django, genau, war auch 2214 und 3018 irgendwie vor kurzem raus.
Ja.
Ja, also Django ist ja jetzt hart eine Entwicklung zu 3.1.
Und das ist ja das, worüber wir heute reden wollen, weil es halt tatsächlich diese Asyn-Features jetzt gibt.
Oder hast du noch mehr News aus der Szene?
Ja, ich habe hier aus dem Wintergarten, beziehungsweise aus meinem persönlichen Setup.
Und zwar, genau, ich weiß nicht, ob man es hören kann, aber jetzt ist ja irgendwie, wir haben irgendwie eine große Bohrmaschine genommen und dann irgendwie hier mal die Außenmauer durchlöchert und jetzt ist hier tatsächlich kabelbasiertes Netz.
Oh.
Und es könnte sein, dass es jetzt deutlich weniger knackst als vorher.
Also du meinst, weil es weniger Wi-Fi gibt und jetzt mehr konstantes LAN.
Jetzt gibt es halt richtig ordentliches.
Stimmt, ich sehe jetzt sogar da hinten das Kabel, das durch die Wand in den Raum
geht, ja.
Genau.
Und eine zweite Geschichte, die ich auch, die auch neu ist und die gar nicht so, die ich eigentlich ganz cool finde, weil ich nicht wusste, also ich habe das Ding einfach mal so auf Verdacht gekauft und wusste nicht, ob es geht, aber es ging tatsächlich.
War, ich habe ja so eine Cull-Digit Docking Station besorgt.
Eine was?
Cult-Digit.
Ja, ja, ja.
Kann ich auch, äh, das Ding versorgt gleichzeitig einen Rechner mit Strom, macht irgendwie Thunderbolt und du kannst Monitore dran anschließen und was ich halt da drin habe, also ich habe sozusagen nur noch ein Kabel, das ich halt immer an Rechner stecke, weil ich hatte nämlich jetzt, da ich jetzt Netzwerkkabel habe, das Problem, okay, wie mache ich denn das Netzwerkkabel an meinem Rechner fest, der hat ja nur OSBC, da so irgendwie so einen Dongle dran zu hängen, der dann so rumbaumelt, ist auch irgendwie etwas unschön, zusätzlich zu den ganzen anderen Adaptern und Dongel, die da sowieso schon rumbaumeln
Und das geht damit halt ganz gut, weil da steckt man einfach Ethernet hinten rein, plus irgendwie das, was zum Monitor geht und hat halt nur ein einziges Kabel, was zum Laptop geht und alles andere geht dann über die Docking Station.
Und was tatsächlich funktioniert, ist, man hat halt, ich habe da hinten halt dann noch Storage dran und das ist schnell, also so eine externe SSD und das ist ordentlich schnell.
Der Monitor ist so ein ultrafein LG 5K und da dachte ich auch so, uh, ob das funktioniert, aber es funktioniert tatsächlich und es kommt auch noch Strom durch.
Also ich suche auch so was ähnliches, ich möchte gerne meine Monitore umschalten mit so einem Switch und zwar einmal auf eine Workstation und einmal auf meinen normalen Rechner.
So eine KVM-Switch oder.
Ja, genau, eine KVM-Switch.
Und ich habe mich dann gefragt, ob es da was Vernünftiges für gibt, weil ich hätte gerne auch die Auflösung der Monitore und mit Switch immer hin und her geschaltet zwischen allen Setups.
Das finde ich cool, aber ich habe noch keine Ahnung, was es da gibt.
Also falls ihr da irgendwie einen kennt oder irgendwie einen guten Tipp habt, hätte mit.
Ich freue mich drüber.
Schau ich mir an und probiere das mal aus.
Ja.
Ansonsten habe ich nichts mehr.
Johannes, hast du eine News?
Eine Neuigkeit?
Nee, ich bin immer noch im WLAN, ich habe kein Kabel.
Du bist tatsächlich ein WLAN bei dem Haus.
Ja.
Ja, das geht ja nicht anders.
Das ist zu
weit weg.
Rohrmaschine.
Ja, nicht in dem Mieters.
Ja, und ich habe schon lange so ein relativ altes Lenovo-Dock.
Das gehört auch gar nicht zu meinem Rechner, sondern das habe ich einfach mal irgendwo gekriegt für, keine Ahnung, 50 Euro oder so.
Und das ist super.
Dock ist total gut.
Er ist einfach alles eingesteckt, Tastatur eingesteckt, Maus eingesteckt, Monitore eingesteckt, Web Storage ist auch dran.
Das ist total gut.
Und du sitzt diesmal auf einem anderen Stuhl und auf einem anderen Schreibtisch, vor einem anderen Schreibtisch in deinem Büro.
Nee, nee, ich habe ja zwei Arbeitsplätze in einem Büro.
Ja, genau.
Der andere Arbeitsplatz.
Er ist ja Pratikanten nicht zu Hause, na gut.
Es gibt den mobilen Arbeitsplatz und den immobilen Arbeitsplatz.
Und das ist jetzt der Festinstallierte mit den großen Monitoren.
Okay, okay.
Ja, cool, okay.
Also dann würde ich sagen, von mir aus
Dango Async tatsächlich.
Minuten zum Thema zu kommen.
Ich bin absolut begeistert, dass es eine Weltpremiere hier heute, liebe Hörerinnen hört.
Hört zu, ihr wisst genau, was euch jetzt erwartet.
Weltpremiere Dango Async 3.1.
Ja, also genau, ich habe direkt mal eine Frage.
Ja, was ist denn der Async.
Du hast eine Frage.
Muss ich irgendwas beachten, wenn ich jetzt von 3.0 auf 3.1 upgrade?
Muss ich dann irgendwas machen oder geht es dann auch noch alles?
Das geht alles genauso wie vorher auch, wenn man nichts in der
nächsten Frage direkt geklärt.
Okay.
Also erstmal nochmal so, vielleicht ganz kurz so zum Frame, zum Rahmen, worum es eigentlich geht.
Was Async dann überhaupt bedeutet und was man jetzt damit machen kann, was vorher schon so ging, so diese Geschichte und wofür man das eigentlich braucht.
Also womit wollt ihr denn anfangen?
Vielleicht, was das ist, Async überhaupt, und ja, dann kommt man vielleicht darauf, warum man das vielleicht haben wollen, möchte oder vielleicht auch gar nicht braucht.
Ist gleich eine schwierige Frage am Anfang.
Also ich glaube, ich würde tatsächlich mit der Motivation starten, weil sonst wird das direkt so trocken und alle Leute schalten sofort auf Durzug.
Und wenn man so zumindest weiß, dass es irgendwie vielleicht nicht so super unwichtig ist und dass tatsächlich irgendwie interessante Dinge damit gehen, dann vielleicht hält man ein bisschen länger durch.
Also du weißt schon, dass da Eve Online schieben, das ist ja gleich sonst ein Exkurs von mir zu Eve Online.
Ja, genau, das wäre interessant.
Aber die sind doch noch auf 2.7, die kriegen dann ja doch die ganzen Sachen gar nicht mit, alle.
Ja, ja.
Aber es gibt wahrscheinlich Gründe, warum das so ist.
Also okay, ich fange einfach mal an.
Codezeilen, das ist der Grund.
Eve Online hat von Anfang an gesagt, wir haben, keine Ahnung, 15 Millionen Codezeilen in unserem Server, wir fangen jetzt nicht an, die auf Python 3.
Also genau, Eve Online basiert viel auf Python, falls ihr das noch nicht
gehört haben.
Ja, genau.
Und soweit ich weiß, ich weiß nicht, ob das immer noch so ist, aber soweit ich das gehört habe, auch auf Stackless Python.
Oh ja, das ist interessant.
Da umgehen sie ja gleich viele von den Dingen, was Hypner Async überhaupt braucht.
Ist gespannt.
Genau, also naja, beziehungsweise zu der Zeit, wo sie das gemacht haben, gab es das halt einfach noch nicht.
Und Stackless Python war halt eine Lösung für die Probleme, die sich da gegeben haben.
Und ich meine, gut, wir können auch einfach direkt so einsteigen, weil das ist ja auch eine gute Motivation, warum braucht man sowas überhaupt?
Und was die wohl können, also hieß es jedenfalls, ist, dass da das alles in Stackless Python halt auch so User-Userland-Shreads oder Green Threads oder wie auch immer man die nennt, sind Stackless Python halt im Grunde brauchst keinen Execution-Stack dafür im Betriebssystem, kannst du die, was Stackless Python kann, ist, du kannst diese Threads pickeln.
Ja, vor allem kannst du mehr als einen ausführen, oder?
Ja, ja, ja, ja.
Das auch.
Du kannst ganz kurz.
Das hat auch keinen Global Interpreter-Log, das heißt, du kannst ja einfach.
Also er kennt ja jetzt gerade wieder die ganze Bagage mit dem Garage-Collector in den Müll.
Moment!
Stopp, stopp, stopp, da sind wir doch noch nicht.
Also ich wollte eigentlich nur mal kurz, warum man das eigentlich machen will.
Was das Coole daran ist.
Also dadurch, dass diese Threads, also wenn ich jetzt zum Beispiel halt einen, wenn ein Ding, ich habe keine Ahnung, wie die Eve Online funktioniert, ja, ich kenne das nicht gar nicht, aber ich glaube, es geht irgendwie um Raumschiffe und so, die da irgendwie im Weltraum rumfliegen.
Excellent, Excellent Space.
Gut, wem es Spaß macht, aber naja.
Und du hast jetzt irgendwie einen Thread, der jetzt ein Raumschiff simuliert, das da durch die Gegend fliegt, dann kannst du halt das Ding pickeln, in eine Datenbank schreiben, irgendwo anders wieder auspacken und weiter ausführen.
Ja, das ist eine sogenannte
Continuation, wäre das, wenn man die akademische Fachliteratur
holen möchte.
Und das ist natürlich, wenn man ein Riesenuniversum hat, vielleicht eine ganz gute Sache, wenn man das machen kann, weil eventuell will man halt nicht alles immer laufen haben, sondern möchte halt auch Teile von dem Universum, das man simuliert, da irgendwie auch einfach mal einfrieren und eine Datenbank schreiben und erst dann wieder auftauen, wenn man es tatsächlich benötigt, damit man nicht immer alles gleichzeitig ausführen muss oder so.
Wäre eine gute Idee für Zeitreisen.
Aber wir wollten über die Motivationen nochmal dann doch vielleicht kurz, lass das doch mal kurz heute ausnahmsweise strukturierter angehen.
Und dann gehen auch versprochen wieder zu Eve Online zurück.
Ja, also genau.
Die Frage ist halt sozusagen, ob nicht es valide Use Cases gibt für so sehr, ja, das deutsche Wort ist so ein bisschen doof, aber ich weiß auch nicht, ob man da ein besseres finden kann, nebenläufige, also massiv nebenläufige Programme.
Also man braucht halt viel Concurrency.
Also es gibt Probleme, da hat man halt einfach ganz viel Concurrency.
Und die Frage wäre halt, muss man, um, wenn man so ein Problem hat, damit umgehen zu können, die Sprache wechseln oder nicht?
Das ist sozusagen, also diese, ich würde sagen, das ist so das Zentrale, ich habe mir ein paar Sachen angeguckt und das zentrale Ding, was, das hat auch Tom Christie quasi in einem Vortrag auf der DjangoCon 2019, als er irgendwie einen, den er dann gehalten hat, so gesagt, also es gibt da diverse Geschichten in Go, es gibt diverse Geschichten in Node.js oder Rust und Erlang.
Erlang ist auch ziemlich en vogue gerade.
Und die Frage wäre halt, wenn man jetzt so einen Use Case hat, bedeutet das, dass man die Programmiersprache wechseln muss und dann halt Erlang machen muss?
Oder halt kann man das auch in Python machen?
Eigentlich kann man das auch in Python machen.
Ist jetzt sozusagen dafür die Infrastruktur noch nicht so ausgeprägt wie in anderen Sprachen, aber so prinzipiell geht das schon.
Und man könnte jetzt sagen, na gut, das hat ein bisschen lange gedauert, also die Geschichte von Async in Python ist wahnsinnig lang.
Aber in letzter Zeit wird es halt sehr interessant.
Zum Beispiel, wenn man jetzt Django als Beispiel nimmt, dann geht das jetzt gerade erst so richtig.
Und der Grund dafür ist, dass eigentlich will man die Syntax dafür haben, so für KuroTinen.
Und die ist erst seit 3.5 so richtig in der Sprache.
Das heißt, vor 3.5 ist sowieso schwierig.
Und Django hat mit Version 1.111 noch Python supportet.
Dann geht es auch nicht.
Das kann man einfach vergessen.
Und mit Django, glaube ich, 2.1, 2.2, weiß ich gar nicht mehr, aber noch Python 3.4.
Und mit 3.4 geht es auch nicht.
Das heißt, sozusagen, also bis vor kurzem konnte Django das gar nicht mit der nativen Syntax machen, weil Python-Versionen supportet wurden, bei denen das einfach nicht geht.
Und jetzt ist es halt sozusagen alt und abgehangen genug, dass sozusagen nur noch Python-Versionen unterstützt werden, mit denen man das dann tatsächlich nativ machen kann.
Und jetzt kann man halt anfangen, das umzustellen, dass beziehungsweise die Arbeiten daran sind ja jetzt auch schon was älter, aber jetzt ist es halt so weit, dass es tatsächlich mal in einer verwendbaren, für User verwendbaren Form halt tatsächlich in Jaro ankommt.
Und wenn man sich sowas, also was ich zum Beispiel mir angeguckt habe, Erlangen ist ja relativ envoch gerade, beziehungsweise halt so ein Dialekt, der oft dann so für Webentwicklungsgeschichten benutzt wird, Elixir.
Und da gibt es auch so ein Web-Framework Phoenix zum Beispiel, und da kann man auch mal angucken, so Phoenix, Elixir, LiveView, solche Sachen.
Und das sieht alles schon sehr beeindruckend aus und ist auch sehr nett.
Und die Frage wäre halt, kann man sowas in Django und Python auch machen?
Und die Antwort ist eigentlich, eigentlich ja.
Eigentlich gibt es nichts, was einen davon prinzipiell abhält.
Außer dass es halt nicht, dass noch nicht so unterstützt wird in den Frameworks und so.
Aber prinzipiell geht das schon.
Was macht das denn jetzt so weich so schön?
Ja.
Keine Ahnung, habt ihr noch was?
Oder wegen Motivation oder was?
Ich überlege gerade, ob ich jetzt schon ausreichend motiviert habe.
Ja, da fehlt noch ein kleines bisschen vielleicht.
Warum man das vielleicht macht, könnte man bei der Motivation direkt sagen.
Also was für Problemfälle gibt es denn, die man jetzt.
Ja, es gibt ja schon immer, also ich meine, es gibt ja schon immer Anstrengungen, Sachen zu parallelisieren.
Und da gibt es ja auch schon ganz alte Sachen.
Nur damals war es halt, damals, ja, also vor zehn Jahren, war es halt noch so, dass Computer zwei Cores hatten oder vier Cores.
Und da ist es nicht so wild, wenn du nur einen benutzt.
Aber heutzutage ist es ja relativ leicht, gerade im Serverbereich Sachen zu kriegen, die 64 oder 928 Cores haben oder noch mehr, wenn man das unbedingt haben möchte.
Oder wenn man auf Grafikkarten geht, kriegt man direkt in die Tausenden von Cores.
Deshalb ist das, man darf da nicht so viel Python verdammen, weil das einfach vor zehn Jahren noch kein Problem war.
Ja, Moment, Moment, aber das ist jetzt, glaube ich, ein anderes Problem, oder?
Also, oder aus meiner Perspektive ist das ein anderes Problem.
Generell die Nebenläufigkeit, die man da jetzt braucht.
Aber das braucht man nicht.
Das haben die anderen auch nicht.
Also das kriegst du auch auf Node.js nicht hin.
Das mag sein, dass du es mit Node.js nicht unbedingt hinkriegst.
Aber mit C und Alang und Rust und so weiter kriegst du die Sachen schon hin.
Und Go auch.
Ja, gut, gut.
Okay, da kriege ich mit Google.
Die nutzen schon alle Cores aus.
Ja.
Aber da weiß ich gar nicht, ob man das unbedingt braucht.
Also, oder da würde ich jetzt denken: so, ja, aber warum?
Doch, also ich glaube, das wäre schon, für mich ist das schon eine Motivation, mehr Cores ausnutzen zu können.
Geht zwar nicht mit Async, das weiß ich, aber ja.
Generell, der Trend geht dazu, mehr Sachen gleichzeitig zu machen, um es mal so auszudrücken.
Und dafür braucht man halt irgendeine Syntax.
Und klar, es gibt die ganzen Sachen, die man früher machen konnte.
Da gibt es die coolen Message-Passing-Interfaces und die coolen, was weiß ich, verteiltes Rechnen Ansätze und dann gibt es Threads und Multiprocessing und so weiter.
Aber die haben halt alle ihre Nachteile und die haben halt alle ihre, sagen wir mal, Schwierigkeiten.
Und gerade wenn man anfängt mit Threads zu arbeiten in Python, merkt man halt sehr schnell, dass das von der Geschwindigkeit her überhaupt gar nichts bringt.
Also ist die Frage, was man Gegenteiligkeit meint, ja.
Wenn es um quasi, wie schnell kann man Dinge machen, geht, dann bringt einem das nichts, sondern macht ja Sachen langsamer.
Wie viele Sachen gleichzeitig kann ich machen?
Wir helfen einem Threads in Python ja erstmal gar nichts.
Ja, wobei, da würde ich direkt unterscheiden zwischen Sachen parallel machen, das heißt, und Sachen concurrent unternehmenläufig, ja, weiß nicht, ob das eine gute Übersetzung.
Die deutschen
Worte sind da total blöd.
Weil parallel geht halt in Python nicht, aber ich glaube auch nicht, dass das das Problem ist.
Oder geht noch nicht.
Es gibt verschiedene Workloads, glaube ich, die du da bedenken musst.
Also Multiprocessing.
Dann hast du ja schon Parallelität.
Mit Numpy kriegst du die Course schon alle voll.
Ja, ja, klar, genau.
Insofern würde ich sagen,
lass uns auch nicht noch erklären, was Parallelität und
Nebenläufig kommt.
Ja, bitte, bitte.
Einmal kurz da einchecken.
Ja, weil es jetzt Multiprocessing nicht sowas wie parallel oder?
Ja, Multiprocessing ist parallel, aber auch das kannst du nur machen, wenn du wenig Kommunikation hast, weil du da eben zwischen Prozessen kommunizieren musst und das ist langsam.
Das ist generell die Krankheit, die du hast, wenn du solche Message-Passing-Sachen machst.
Das hatten wir ja früher auch schon gehabt, ja.
Als ich studiert habe, hatten wir einen großen Cluster, der hatte 16 Prozessoren an der Universität.
Und es waren halt unterschiedliche, es waren halt 16 Computer, die mit dem schnellen Netzwerk verbunden waren.
Aber je weniger Nachrichten du schicken konntest, schicken musstest, umso schneller war das dann halt.
Das heißt, im Wesentlichen warst du da gar nicht durch die CPU gedrosselt, sondern eben durchs Netzwerk.
Ja, ja, gut.
So ein bisschen ist es bei Multiprocessing ja auch.
Je weniger Nachrichten du schicken musst, umso besser.
Aber da muss man
schon gar keine Schicken.
Irgendwie, dass man sagt, man hat so eine gemeinsame.
Genau, es gibt Private Memory, kannst du einfach verwenden.
Wo ist du gar keine Nachrichten schicken?
Oh cool, sowas gibt es schon?
Ja, ja.
Ist auch eingebaut.
Seit Falten 38 ist das drin.
Also du kannst sagen, hier irgendwie.
Aber dann hast du trotzdem noch irgendwelche Locking-Mechanismen, die dir da unter halt warten mussten,
wo der.
Aber wenn du CPU gebraucht bist, ist das alles nicht mehr schlimm.
Also das ist alles.
Also ich würde sagen, eben, wenn du, wenn du Dinge einfach, wenn du jetzt irgendwie Number Crunch-Geschichten machst, da alle Cores zu verwenden ist in Python, überhaupt kein Problem.
Das geht super.
Das ist tatsächlich einer der beliebtesten Anwendungsfälle für Python.
Also ich würde sagen, Data Science, da ist Python wahrscheinlich die Sprache Nummer 1 gerade.
Und das ist ja hauptsächlich so ein Zeugs und da geht das super.
Da werden immer alle Cores benutzt, gar kein Problem.
Ja, klar.
Im Wesentlichen ist der Trick halt daran, dass die aus Python rausgehen, die Bibliotheken, und dass die das halt in C machen.
Ja, oder du kannst halt auch so Dinge machen.
Nee, nee, nicht nur.
Also du kannst auch pure Python-Funktionen parallelisieren, also mit auch über mehrere Maschinen hinweg.
Achso, ja, okay.
Aber das ist, also ich habe hier gerade die.
Also das ist jedenfalls Parallelität.
Ja, genau.
Man macht Berechnungen auf mehreren Prozessoren und die sind gleichzeitig im Wesentlichen.
Und es spielt keine Rolle, ob es die gleiche Berechnung ist oder eine andere Berechnung.
In den meisten Fällen werden es ja die gleichen Berechnungen sein.
Und dann fällig
auf allen Zeilen dieser Tabelle möchte ich jetzt ausgerechnet haben, was die Summe ist.
Genau, genau.
Also ich habe hier die Definition von Rob Pike, von demjenigen, der sich da Google mal ausgedacht hat, und der sagt halt, Concurrency is about dealing with lots of things at once.
Parallelism is about doing lots of things at once.
Not the same, but related.
One is about structure, one is about execution.
Concurrency provides a way to structure a solution to solve a problem that may, but may not necessarily be parallelizable.
Ja, das hätte ich gleich erstmal.
Ich dachte, das hätte ich gerade.
Nee, das fand ich jetzt auch nicht arg erhellend, aber.
Verdammt, na gut.
Ja, Parallelität ist halt Sachen gleichzeitig machen und Concurrency ist verschiedene Sachen machen.
Das ist
irgendwie es in meinem Kopf ist.
Ja, eine Erklärung, die ich mal gehört habe, vielleicht ist die besser, vielleicht einfach mal ein paar durchprobieren, ist, dass halt sozusagen ein Bartender kann irgendwie viele Kunden concurrent behandeln, sozusagen, oder wenn ein Bartender viele Kunden hat, die er mit Drinks beschickt, dann ist das irgendwie Concurrency.
Aber wenn du mehrere Drinks gleichzeitig machen willst, brauchst du mehrere Bartender.
Da erreicht einer nicht.
Und das wäre dann Parallelität sozusagen.
Mehr Arme für den Barkeeper.
In meinem Kopf geht es, bei mir geht es ganz viel um das Wort gleichzeitig.
Parallelität heißt, du machst mehrere Sachen gleichzeitig.
Und Nebenläufigkeit oder Concurrency heißt, du machst mehrere Sachen nebeneinander her.
Also nicht an einem abschließenden Schritt erstmal den einen Drink mixen, sondern fünf Gläser hinstellen und jedes Mal einmal ein bisschen Zitrone rein und dann ein bisschen Drink.
Genau, genau.
Genau, also du hast fünf Drinks, die du zubereiten musst und du machst halt die Schritte von denen und du machst erstmal was an dem Drink 1, dann machst du was an Drink 2, dann machst du was am Drink 3.
Und am Ende sind alle fertig.
Die Gesamtzeit ist dann ja genau dieselbe, als würdest du fünf Einzel machen.
Nur du hast halt dann fünf gleichzeitig rausgeschickt und alle haben quasi die Zeit gewartet, anstatt dass du jeweils erstmal den ersten, den zweiten, den dritten, den vierten, den fünften.
Genau, also
du machst eventuell die Latenz geringer.
Du kannst schon mal die Bestellung des nächsten annehmen, während ein Drink noch in Bearbeitung.
Du hast halt den nächsten bearbeiten, während einer noch, weiß ich nicht, während einem in einem irgendeine fürchterliche chemische Reaktion läuft, die halt eine Zeit lang braucht und du wartest halt jetzt nicht stumpf davor ab,
du wartest auf den Assistenten, der genau.
Der Erste, der sonst seinen Drink sofort bekommen hätte, weil er das erste in der Schlange stand, der muss jetzt länger warten, weil er die anderen Drinks auch.
Potenziell, ja.
Ja, potenziell ja, aber guck mal, wenn da gerade kein Eis da ist, dann kannst du ja einen anderen Drink machen, während du aus Eis wartest.
Oder während jemand was aus dem Lager holt.
Das merkt der erste Kunde überhaupt gar nicht, weil der muss so oder so drauf warten, bis sein Eis da ist.
Ja, aber nur dann pausen.
Ja, genau, wenn du Wartezeiten hast.
Und dann bemerkt der erste Kunde gar nichts.
Dann hast du automatisch weniger Latenz und mehr Durchsatz.
Weil du Wartezeiten nutzen kannst.
Und das ist ja eine der großen Allyren dieser ganzen Geschichte, dass du sagst, okay, ich muss jetzt eh auf eine Datenbank warten oder auf die Dateien, die du hast.
Ja, also sobald drei Leute arbeiten.
Also nicht nur den Barkeeper, sondern auch noch die Küche und du möchtest immer einen Drink mit Essen servieren oder sowas.
Und solange du das noch nicht vergessen, dann kannst du alle anderen Drinks schon fertig.
Wenn du Abhängigkeiten hast oder wenn du Wartezeiten hast, die kannst du überbrücken, indem du was anderes machst.
Und das ist Concurrency.
Du machst nie Sachen gleichzeitig.
Du hast, der Bartender kann nie mehrere Sachen gleichzeitig machen.
Genau, der hat halt nur einen Prozessor.
Aber der kann mehrere Drinks gleichzeitig zubereiten.
Das heißt nicht, dass er überall gleichzeitig was reinschüttet, aber das heißt halt, dass die alle zu einem gleichen Zeitraum in Bearbeitung sind.
Genau wie der Jochen sagt, wenn du fünfmal so viele Drinks machen willst, brauchst du halt mehr Bartender und die sind dann auch erstmal unabhängig voneinander.
Es gibt jetzt leider keine SIM-D-Bartender, die mehrere Sachen identisch gleich machen können, dann könntest du fünf identische Drinks gleichzeitig zubereiten.
Da bist du jetzt keine Analogie in der echten Welt.
Aber ich habe jetzt keine.
Async ist jetzt Parallelität oder?
Nein, Concurrency.
Also ich würde sagen, dieses Problem Parallelität ist, also dass du beides gleichzeitig brauchst, diesen, also das wäre jetzt mal eine steile These.
Ich weiß jetzt auch wieder, wann wir das schon mal hatten und ich glaube, es war tatsächlich in der ersten Sendung, wo ich halt auch meinte, so eigentlich diesen Fall, dass man beides hat, dass man Concurrency braucht und Parallelität, den hat man fast nie.
Also, oder mir fällt es schwer, mir dafür ein Use Case auszudenken.
Die anderen Fälle hat man, dass man viel Concurrency braucht, hat man, dass man viel Parallelität braucht, hat man, aber beides zusammen.
Ja, also ich habe mir viele Sachen vorstellen.
Also wenn ich jetzt beispielsweise autonomes Fahren oder Sport oder sowas.
Und gleichzeitig fahren bestimmte Menschen übers Eis oder sowas.
Man merkt vielleicht immer, glaube ich.
Und dann musst du prognostizieren, was dann passiert.
Das ist echt ein Parallel für jeden Einzelnen, um dann irgendwie eine Vernunft zu machen.
Nee, aber das ist nicht konkret.
Du machst das Autofahren gleichzeitig.
Nein, es fahren alle gleichzeitig und alle müssen ja gleichzeitig darauf reagieren.
Und du möchtest ja für alle gleichzeitig irgendwie Werte haben und möchtest dann das
ganze Team.
Das verstehe ich nicht.
Tut mir leid.
Also du hast mehrere Fahrzeuge und.
Mehrere Fahrzeuge.
Du hast so über einen Rechner, auf den mehrere Fahrzeuge fahren?
Das verstehe ich nicht.
Genau, du hast die ganze Flotte, die du steuerst.
Ja, aber die sind ja überall.
Also ich würde sagen, das ist ein Anwendungsfall, den kenne ich sonst nicht.
Aber du hast ein gegnerisches Team und das ist halt nicht unter deiner Kontrolle.
Und du möchtest dann trotzdem alle deine Leute, deren Informationen du dann halt gerade erst live einlesen kannst, dazu bringen, dass sie parallel die Entscheidung treffen, die vernünftig statistisch wahrscheinlich ja zu besseren Ergebnissen funktioniert, irgendwie so.
Der Dominik überspringt so die simplen Schritte, dass das nicht so gut ist.
Also selber Farben kann müssen der macht direkt komplett.
Nee, nee, nee, tu mir leid, das würde ich sagen, das ist kein Use Case.
Vielleicht doch.
Da wüsste ich jetzt
auch nicht.
Nee, aber wir können ja direkt mal zu einem super guten Use Case zurückgehen und er heißt Eve Online.
Es sind ganz viele Spieler, die gleichzeitig Sachen machen wollen.
Meine ich doch gerade.
Und du hast aber trotzdem die große Statustabelle auf dem Server, die du die ganze Zeit aktualisieren musst.
Und das musst du parallel machen.
Also ich meine, die Lösung, die Eve Online dafür hat, ist halt, dass sie es langsamer machen.
Habt ihr Eve online mal gespielt?
Nee.
Nee, nur ganz viel drüber gelesen.
Oh, ich habe es tatsächlich auch mal ausprobiert.
Ich muss gestehen, ich war nicht in den Gruppen.
Ja, das habe ich auch nicht geschafft.
Aber es gab tatsächlich tolle Schlachten.
Also man hat dann so Schlachten gehabt, wo dann so 20.000, 30.000 Schiffe verloren gegangen sind.
Und man konnte da ein Business draus machen, weil da konnte man die Schiffe kaufen.
Das war richtig Geld, was da vernichtet wurde.
Ich glaube, in der größten Schlacht wurden mehrere Millionen Dollar an virtuellem Kapital vernichtet, gleichzeitig, weil man irgendwelche Sprungtouren blockiert hat mit riesigen Flotten an Schiffen und alles in die Luft gesprengt hat, was dann in die finden kann.
Ja, das ist das Geschäftsmodell von Eve Online, oder?
Ja.
Also es gibt sogar einige, die Güter kaufen und sie dann verbrennen.
Ja, und du kannst sogar tatsächlich, aber in diesem Spiel konntest du halt Echtgelddinger generieren, die du halt verkauft hast und wenn du halt ein Wirtschaftsunternehmen Imperium in diesem Spiel aufgebaut hast, es gab einige Leute, ich glaube, die haben über 20.000 Dollar oder sowas im Monat gemacht, indem die für Eve Online Businesses gebaut haben, die sie da durch die Sachen geschleust haben.
Und wenn du dann halt sowas gemacht hast, wie einen Krieg zu organisieren zwischen den einzelnen Fraktionen, die sich gegen das in die Luft sprengen und das halt alles auf dieser spielerischen Karte, ohne dass das das reale Auswirkungen hätte, also außer wirtschaftliche jetzt für die Jungs, die da vor dem Gegner saßen, dann war das schon irgendwie sehr faszinierende Geschichte, die da irgendwie losging.
Aber guck mal, Jochin, das ist doch ein perfektes Beispiel, oder?
Du hast ganz viele Spieler, die gleichzeitig Sachen machen wollen und du musst aber diese Statustabelle, das ist parallel.
Ja, also ich würde sagen, du musst Spiele auf die Spiele.
Also ich bin jetzt, ich bin nicht so der Spielexperte, insofern, keine Ahnung, aber ich würde jetzt mal sagen, Spiele, die sind alle nur Concurrent.
Da brauchst du nichts.
Wenn ich jetzt so spiele, ein ganz klassisches Killerspiel, ja, und wir schießen beide gleichzeitig aufeinander.
Wo brauchen die denn, wo brauchen die Spiele denn bitte CPU?
Die brauchen überhaupt gar
keine CPU.
Nee, nee, die Spiele heutzutage laufen nicht mehr auf dem Client, sondern die laufen komplett auf dem Server.
Ja, selbst wenn die komplett auf dem Server laufen, wo brauchen die denn CPU?
Diese vorher genannte Battle, die der Dominik genannt hat, wo dann 30.000 Leute teilnehmen, der Trick an der Sache, warum das funktioniert, ist, dass die den Timestep runterdrehen.
Das heißt, in so einer Zone, wo viele Leute gleichzeitig sind, die müssen auf einem Server sein, damit diese Zone kohärent ist.
Aber wenn da zu viele Leute sind, dann tun die so, als ob die Zeit langsamer läuft.
Weil der Rechner ausgelastet ist.
Wenn die diese Status-Updates parallel abarbeiten könnten auf einem Rechner, dann müssten sie das nicht machen.
Die gehen ganz, ganz hart ans Limit, weil die Simulation auf dem Server passiert.
Also zum Teil passiert die Physiksimulation auf dem Server.
Das ist im Wesentlichen.
Genau.
Man hört ganz oft, dass so und so viele Millionen Leute gleichzeitig in Fortnite sind.
Aber das stimmt nicht.
Es sind nur höchstens 50 auf einer Instanz.
Jetzt gab es so Events, wo 10 Millionen Leute bei so einem Konzert dabei waren.
Das ist ein kleines bisschen gelungen.
Das waren 250.000 Instanzen mit jeweils maximal 50 Leuten, weil da einfach die Auslastung, da ist der Prozessor voll.
Und da ist Parallelität einfach.
Okay.
Je mehr Parallelität du nutzen kannst, umso höher kannst du diese Zahl setzen.
Und umso weniger Instanzen brauchst du.
Und das ist richtig, da ist Geld drin und da ist richtig.
Okay, gut, vielleicht falls ich einfach nur nicht gut genug, wie Spiele funktionieren, dass ich mir nicht so richtig vorstellen kann, warum die jetzt CPU braucht.
Also die Frage ist halt, wo ist halt der Flaschenhalt?
Ist der Flaschenhalt tatsächlich CPU?
Das ist dann irgendwie sowas wie Netzwerk.
Ich glaube, eher Konkurrenz und Netzwerk, ja.
Da müssen wir Peer-to-Peer vielleicht machen.
Das würde vielleicht schneller gehen, wenn man das irgendwie über Peers verteilt, als dass man irgendwie alles durch einen Hals schickt.
Bei allen Leuten irgendwie so ein bisschen.
Es gibt sicher viele Flaschenhals.
Ich glaube, da ist alles, das ist so ein blödes Problem.
Wenn du ein Problem gelöst hast, dann kommt direkt das nächste.
Wenn du einen Kopf abgeschlagen hast, wachsen direkt vier neue Reiter.
Okay, drauf.
Okay, ich gebe zuerst.
CPU ist da einfach einer der Faktoren.
Vielleicht ist es bei Spielen so.
Ich kann es noch nicht so richtig nachvollziehen, aber okay.
Also ich meine, mir fällt auch ein Beispiel ein und das sind halt Datenbanken.
Da hast du nämlich auch beides.
Da hast du halt sowohl I.O., du hast halt, weiß ich nicht, 30.000, 40.000 Requests pro Sekunde und die machen alle CPU.
Die machen alle irgendwie Geschichten in irgendwelchen Bio-Tree's irgendwie durchsuchen, irgendwelche Indizes abgleichen, Hashes ausrechnen.
Je mehr du gleichzeitig machen kannst, umso besser.
Genau, also an der Stelle würde ich sagen, also Datenbank zwar beim Python-Schreiben, vielleicht nicht die beste Idee, weil das wird schwierig.
Wie ist es mit Videostreaming?
Auch das ist sowas, wo.
Würde ich sagen, komplett Concurrence.
Da gibt es nichts, was irgendwie ein CPU braucht.
Na klar.
Also außer du kodierst irgendwie um.
Aber musste.
Du musst die kodieren.
Jeder moderne Streaming-Dienst, also wir sind jetzt hier auf Ware bei, aber wenn du auf BBB gehst oder auf Zoom oder auf sonst was, die kodieren alles um.
Tatsächlich machen die Größenskalierungen, damit die Clients mit unterschiedlichen Bitraten versorgen können.
Und das kannst du entweder auf dem Client machen, was nicht super zuverlässig ist, weil die Clients halt auch Mobilgeräte sein können.
Ja, du machst es auf dem Server und dann bist du ganz knallhart.
Ja, aber da würde ich sagen, da bist du doch sowieso schon, da ist der Flaschenhals auch wieder woanders, weil dadurch, dass du, dass die einzelnen Clients, die du dran hast, so fett sind, macht es doch überhaupt nichts, wenn du pro Verbindung dann halt was Fettes aufmachst, wie einen Prozess oder so.
Ja, klar, aber du hast auf jeden Fall Parallelität, du musst auf jeden Fall.
Ja, ja, gut, aber dann hast du.
Du hast halt nicht Streams, oder wie viel das?
Wenn du sagst, es gehen nur 50 Leute auf eine Instanz oder es gehen nur 50 Streams über einen Rechner, dann machst du halt für die 50 Prozesse auf, gar kein Problem.
Geht das auch mit Parallel?
Ja, klar, klar, aber das ist dann schon Parallelität.
Also du machst schon viele Sachen gleichzeitig.
Ja, aber einen Fall, nachdem ich suche, ist, wo du ganz, ganz viel Concurrency hast, so viel, dass du das mit Prozessen nicht erschlagen kannst, weil es zu viele sind.
Also sagen wir mal, du hast 10.000 Verbindungen gleichzeitig.
Aber das kannst du ja bei deinem Webserver locker mal haben.
Ja, natürlich, genau.
Also du hast 10.000 gleichzeitig, die auch gleichzeitig aktiv sind.
Nicht nur einfach eine Verbindung, die rumliegt, sondern tatsächlich aktiv, über die irgendwas drüber geht.
Wenn du das Problem hast, plus irgendwie CPU, dann kannst du es vergessen.
Dann kriegst du es mit Python nicht mehr hin, weil Prozesse kannst du dann nicht mehr nehmen.
Ich glaube, du hast das ausprobiert.
Hast du mal so eine Story erzählt auf deinem Mac und hast da keine Panik verursacht, kann das sein?
Ja, das habe ich jetzt im, genau, im Zug.
Aber das waren Threads tatsächlich, keine Prozesse.
Also mit Prozessen wird das gar nicht gehen.
10.000 Prozesse ist nicht möglich, aber Threads sollten eigentlich möglich sein.
Nicht auf dem Mac.
Und ich habe es tatsächlich dann auch mal auf dem anderen ausprobiert, um auszuschließen, dass irgendwie meine Hardware leicht defekt ist oder so.
Nö, auf Catalina macht das halt sofort Kernel Panic, wenn man 10.000 Threads aufmacht.
Und also das, also die Lüfter gehen kurz an und dann ist der Rechner aus.
Ich habe da tatsächlich auch ein Bug bei Apple aufgemacht.
Das geht ja so nicht.
Ja, gut, aber ich meine, wenn man da in die Ecken und Kanten geht, dann ich habe auch schon mal meinen Mac mit einem Programm abgeschützt, was nur das Clipboard ausgelesen hat.
Ja, es ist ein Kartenhaus.
Ein großes Kartenhaus.
Tatsächlich, ich habe es dann jetzt auch nochmal auf Linux probiert und so und da geht es problemlos.
Gar kein Ding.
Ja, gut, aber so eine Situation, wurde 10.000 Threads, also ich meine, das ist dieses berühmte C10K-Problem.
Wie schaffst du das, einen Webserver zu machen, der 10.000 Clients gleichzeitig benutzen, bedienen kann?
Und wenn du das hinkriegst, ist es besser.
Genau, aber dafür würde ich sagen, brauchst du eben keine CPU, sondern das ist rein Concurrency und das geht problemlos mit einem Prozess und einer CPU.
Aber ich finde bestimmt irgendein Workload, wo der noch was berechnen musst.
Keine Ahnung,
Bilderskehr nicht so.
Ich glaube auch, ich glaube, dass es sowas tatsächlich gibt, aber also ist so einfach, dass man sagt, also ich kenne das ja dann immer so, dass Leute sagen, das ist ja gar keine richtige Problemsprache.
Aber ich meine, wer hat solche Probleme?
Also ich hatte sowas, ehrlich gesagt, noch nie.
Also, und ich habe schon eine Menge Zocks gesehen.
Bei mir war es entweder.
Ich glaube, da ist so ein bisschen Selection-Bias auch drin, oder?
Ja, gut, kann auch sein, ja.
Klar, wir haben auch schon viele Sachen gesehen, aber wir sind halt in Python unterwegs und wir sehen nur solche Sachen, die es in Python gibt.
Ja, das mag sein.
Das kann durchaus.
Wie auch immer, aber genau, dieses Parallelität-Ding, also wenn man beides braucht, ist halt schlecht, das ist nach wie vor nicht gut in Python.
Aber sagen wir mal so, dieser Concurrency-Use Case, für den es halt tatsächlich auch Anwendungen gibt.
Und ich meine, Node.js ist, dass einer der Gründe, warum Node.js so populär ist, ist halt, dass das damit besser ging.
Oder dafür halt besser benutzt wurde, äh, besser benutzbar war.
Und ja.
Ja, auch ein besseres Programmiermodell hat, oder?
Also dieses Promises und Futures und so weiter, das ist ja auch nicht.
Das ist alles schon sehr einfach.
Ja, aber das ist eigentlich alles, äh, sozusagen, das gibt es halt in Python ja auch.
Ja.
Was gibt es in der Angebot schon, aber kannst du das kurz beschreiben?
Ja, also gut, okay, also wenn wir jetzt schon im Grunde wissen, okay, wir wollen das eigentlich schon haben und das ist schon cool.
Und das kann ja wohl nicht sein, dass ich jetzt Node.js lernen muss, nur irgendwie um immer noch hip zu sein, dann kann man sich ja, genau, kann man sagen, okay, wie macht Node.js das denn eigentlich?
Weil Node.js hat die gleichen Beschränkungen im Grunde.
Das ist sehr vergleichbar zu Python, hat die gleichen Beschränkungen, da gibt es auch einen Gil, ja, ganz genauso wie in Ruby und Rails und sonst irgendwie in PHP.
Die ganzen Skripts machen das alle so, weil es halt auch sinnvoll ist.
Ja, und weil es halt auch single-threaded die Performance, also wenn du kein Multiprocessing brauchst, also wenn du keine Parallelität brauchst und keine Konkurrenz, dann ist es halt das Schnellste.
Ja, genau.
Und was Node.js macht, ist halt, sie haben halt eine Event Loop und ja, dann kann man halt sozusagen Callbacks auf dieser Event Loop, oder sagen wir so, das ist halt das, was ganz früher schon immer, ich glaube, das ist schon immer möglich, man kann halt Callbacks auf dieser Event Loop registrieren, oder?
Ja, Callbacks sind ja was Schreckliches, das ist doch die Callback hell.
Genau, gar keine richtiges, gar keine lineare Programmierung mehr.
Hast du was gesagt?
Hab ich eher was gesagt, vielleicht gab es ja irgendjemand, der hat was gesagt, der wollte, er wartet noch auf irgendwas.
Wie heißt denn das?
Wie muss ich denn da fragen, wenn ich das wissen will?
Ach Moment, da steht jemand, der hat da rumgegangen gedacht, ach nee, da war doch jemand.
Ach, Moment, da war noch jemand, der hat aber was gesagt, was hat irgendwie ach, Callback, achso, da hinten da, ach nee, da unten A, O.
Das ist ja gar nicht so schlimm, diese Vermischung kriegst du ja immer.
Das Problem ist, dass dein Programm dann auch so aussieht, weil du die Callbacks quasi in der falschen Reihenfolge definieren musst.
Du kannst nicht sagen, erst A, dann B, dann C, sondern du musst erst sagen, erst A und wenn es erfolgreich ist, dann B, ansonsten C und wenn B erfolgreich ist, dann zwischendurch D und E und dann hast du A, B, E, F und C kommt dann irgendwo ganz anders.
Das heißt, du hast dein Programm nicht mehr, kannst es nicht mehr von oben nach unten lesen, sondern du musst es so gemixt lesen.
Und das ist ganz, ganz, ganz blöd zu programmieren.
Und deshalb hat JavaScript dann irgendwann diese Promises und Futures eingeführt, wo du einfach sagen kannst, da kommt jetzt was zurück, was irgendwann fertig ist.
Und sobald du es benutzt, musst du halt warten.
Ja, kann man in Python quasi ganz genauso.
Also seit 3.2 gibt es Concurrent Futures.
Ich weiß nicht, ob sich JavaScript das auch so das Promises-Konzept da so ein bisschen abgeguckt hat.
Also Python hat sich das abgeguckt aus der Java-Welt tatsächlich, wo es dieses Future-Konzept wohl schon länger gibt.
Aber in Java
nennt man doch viel eher Threads, oder?
Da macht man einfach.
Ja, aber man kann auch das machen.
Das ist aber sagen wir so, wenn wir jetzt schon bei den Callbacks sind, oder bei dieser Art, das kann man halt in Python im Grunde auch so machen.
Ja, also das ist auch in dem Async-Teil, also sagen wir so, man muss das wahrscheinlich alles irgendwie ein bisschen voneinander unterscheiden.
Ja, genau, wie das drunter aussieht, was man unten drunter für einen Mechanismus verwendet, um halt Concurrency zu erreichen und was man dann für Abstraktionen darüber verwendet.
Weil tatsächlich ein Async.io jetzt sozusagen die aktuell in der Standardbibliothek befindliche Lösung für diese Concurrency-Geschichte ist halt.
Ja, sogar in der Sprache, oder?
Ist nicht eine Bibliothek, sondern ist richtig in die Sprache integriert mit Schlüsselwörtern.
Ja, aber das ist voneinander, das ist auch eine gute Sache, das ist voneinander getrennt.
Also was in der Sprache tatsächlich drin ist, sind halt Async-Funktionen.
Also die man halt mit Async Dev, Async Dev, genau, und Await.
Mit diesen Schlüsselwörtern kann man da operieren, aber in der Sprache ist nur definiert, dass diese Geschichten dann, wenn du sie aufrufst, halt eine Coroutine zurückgeben.
Es ist nicht gesagt, wie das jetzt alles sonst funktionieren muss.
Der Rest ist halt
Ja, okay, klar, die Event-Loop und so weiter.
Du kannst nämlich, genau, du kannst halt Async Dev und Await auch in Trio verwenden.
Und Trio verwendet ein ganz anderes Modell unten drunter als jetzt Async.io.
Trio.
Das ist eine Implementierung, sowas ähnliches wie Async.
IO, bloß mit einer etwas moderneren und etwas anderer Zielsetzung.
Also es gibt diverse.
Es gibt Async.io, ist in der Standardbibliothek.
Es gibt Curio, das ist das von David Peasley.
Ist halt sozusagen erst ab Python 3.7 und irgendwie so ein bisschen advanster.
Und dann gibt es nochmal die advancedere Version von Nathaniel Smith im Trio.
Und das Ding macht im Grunde sowas ähnliches, aber halt anders.
Also die Art, wie man es programmiert, ist halt anders.
Und benutzt aber Async Dev kann man in all diesen Dingern verwenden, weil das halt nur sagt, okay, wenn ich das so auf, wenn ich so eine Async-Funktion habe, dann gibt die halt eine Coroutine zurück.
Und ich kann jetzt damit irgendwas machen.
Aber wie das Programmiermodell aussieht, was ich dann mit diesen Coroutinen mache, das ist ja demjenigen, der das sozusagen dieses Framework implementiert, überlassen.
Und da gibt es unterschiedliche Ansätze.
Und im Grunde, Trio ist so ein bisschen das zu Ende gedacht, dass das halt so ein Problem ist.
Also das wurde ja auch schon angedeutet mit dieser Callback-Hell-Geschichte.
Also das grundsätzliche Problem, was auch der Autor von Trio da identifiziert hat, also packt da auch mal einen Artikel, wo man das en Detail nachlesen kann in die Shownotes, dass er sagt, das ist alles, also ein großer Teil der Probleme, die wir hier sehen, sind die gleichen Probleme, die Leute früher mit Goto hatten.
Oder er sagt, das ist halt eine sehr gute Analogie dafür.
Wir sind in der Assembler-Welt angekommen, wenn wir über Goto sprechen.
Ja, nee, nicht ganz Assembler.
Es gibt ganz viele Sprachen, die Goto haben.
Also das Problem bei Goto ist halt irgendwie, dass es dir, was das unmöglich macht, ist halt lokal über Code nachzudenken.
Weil du weißt halt nicht, also er hat dann halt so ein schönes Diagramm, wo man halt, er macht halt immer, er geht von einem Goto zu einer Zeile und dann er macht das halt immer weiter, bis du halt siehst, es ist ein riesiges Spaghetti-Knäuel.
Das heißt, du weißt halt nie genau, wie du da eigentlich hingekommen bist und wo es hingeht.
Und zum Beispiel, du kannst halt keine Tracebacks werfen, weil du weißt überhaupt nicht, wo du herkommst eigentlich.
Und das ist halt relativ schrecklich.
Du musst halt dann das Programm immer komplett verstehen.
Ansonsten kannst du nicht sagen, was da gerade passiert, weil es keine lokalen Blöcke gibt, die irgendwie, wo man sagt, okay, ich habe jetzt ungefähr verstanden, was das Ding tut.
Das muss ich jetzt nicht noch, also ich brauche nur zu wissen, wie die Abstraktion davon ist.
Und das reicht mir, um damit arbeiten zu können.
Also zum Beispiel irgendwie sowas wie Print, hello World.
Print macht intern was relativ Kompliziertes.
Das macht irgendwie Syscalls, das macht irgendwie alle möglichen komischen Dinge, die halt dann so zu tun sind, wenn man irgendwie, wenn dann was auf der Konsole erscheinen soll.
Aber das muss man alles nicht wissen, sondern man ruft es einfach auf und das geht.
Und Funktionen sind halt super, weil genau die brauchst du ja eigentlich nur zu merken, was das Ding tut, aber wie das intern funktioniert, das muss einen nicht mehr interessieren.
Ist auch die gleiche Idee bei Klassen und so weiter.
Und mit Goto funktioniert das halt nicht.
Weil du nicht sagen kannst, okay, ich weiß jetzt, was hier passiert und muss mir darüber keine Gedanken mehr machen, weil es kann halt passieren, dass dein Code nicht wieder dahin zurückkommt, wo du es.
Also es gibt nicht so einen Aufruf, der wieder zurückkommt, sondern das kann verschwinden und ist halt halt irgendwie weg und es passiert was völlig anderes.
Und das ist halt schlecht, wenn das so ist, weil dann kann man nicht mehr lokal über Code nachdenken.
Und er meinte halt so, ja gut, also in den Ende der 60er, Anfang der 70er, Deikstra und so, da gibt es halt den berühmten Artikel, die haben sich da Gedanken gemacht und deren Vorschlag war halt, ja, Goto rauswerfen, verbannen, irgendwie echten und stattdessen sowas verwenden wie Funktionen und Blöcke und so.
Und das hat sich durchgesetzt und auch lustig, die Gegenargumente damals.
Und auch Knut hat zum Beispiel gesagt, oh nö, ich finde Goto eigentlich ganz gut.
Auch sehr interessant.
Knut?
Ja.
Donald Knut.
Ah, war das nicht der, der sowas gesagt hat wie Pre-mature Optimization is the root of all evil?
Genau, genau, der.
Aber dieses Zitat ist mit sehr viel Vorsicht zu genießen.
Er hat auch gesagt, wenn man 3% rausholen kann, dann ist das nicht mehr premature.
Ja, ja, das ja, andere Zeiten.
Ja, jedenfalls.
Genau, genau.
Der war da auch nicht unbedingt so totaler Fan von Goto rausschmeißen, weil er so eines der Argumente war halt so, ja, das bedeutet ja, wir müssen ganz anders programmieren lernen.
Also wir haben jetzt über Jahre irgendwie, sind wir jetzt, haben wir uns auf ein Level begeben, dass wir damit irgendwie jetzt klarkommen und haben uns so Methoden angewöhnt, wie man damit umgeht.
Und wenn wir das jetzt so ändern, dann müssen wir im Grunde nochmal neu programmieren lernen.
Und vielleicht, das ist wohl so.
Und vielleicht sogar wird es dann sogar langsamer, weil eben diese Funktionsaufrufe und diese ganzen Strukturmechanismen, die brauchen ja auch Rechenzeit.
Ja, natürlich.
Und Speicherplatz.
Genau.
Und wenn du nochmal 3% irgendwo rausholen kannst, dann kannst du das
auch machen.
Und wenn der Mainframe super teuer ist und Arbeitskraft zu verwendet, dann macht es vielleicht sogar Sinn.
Aber heute vielleicht.
Ja, das ist halt, wir stehen auf den Schultern von Riesen.
Wir haben heute genügend Prozessoren.
Wir haben genügend Megahertz, dass wir uns das leisten können.
Ja, und im Grunde, er sagt halt, ja, also bei Concurrent-Programmierung hat man im Grunde in gewisser Weise das gleiche Modell und er sagt zum Beispiel, Futures findet er ganz schrecklich, weil die im Grunde das gleiche Problem haben wie Goto, weil du kriegst halt was zurück, also du rufst eine Funktion auf, du kriegst was zurück, aber du bist ja gar nicht, das ist ja gar nicht linear zurück.
Dein Code geht ja nicht linear weiter, sondern irgendwo anders passiert irgendwas, das aber durchaus Auswirkungen darauf haben kann, was später in dem Code, der dann jetzt sozusagen von dir aus gesehen weiterläuft, darauf Auswirkungen hat.
Das heißt, du musst wissen, okay, der Task, der da noch läuft, der wird später mal irgendwas Böses machen oder vielleicht noch irgendwie, keine Ahnung.
Und da muss ich auf jeden Fall dran denken, dass der mir vielleicht irgendwie dazwischengrätscht oder ich muss hier mal ein Lock setzen, damit das irgendwie atomar bleibt und keine Ahnung.
Das heißt, ich kann nicht mehr sagen, okay, ich habe jetzt diese Funktion aufgerufen, es ist gut, ich weiß jetzt Bescheid, es hat entweder funktioniert oder nicht und ich mache jetzt weiter.
Sondern ja, ich habe dieses Future-Ding in der Hand, aber tatsächlich muss ich mir irgendwie selber merken, was da noch alles passieren kann.
Und das ist natürlich schlecht, weil das macht halt genau dieses lokale Nachdenken über Code kaputt.
Und er sagt halt so, alle Concurrency-Lösungen, die dazu führen, dass man nicht mehr lokal über Code nachdenken kann, das ist alles schlüssig, das ist alles nicht richtig.
Das sollte man so nicht machen.
Ja, und was ist da die Lösung
jetzt davon?
Ja, also er hat da so ein Konzept, das heißt dann irgendwie Nurseries, wo man dann sofort.
Bitte?
Nö, so wie es die kleine Krankenschwester, die die Babys auf der Auto-Station.
Genau, er sagt so, ja, so Tas oder sind sowieso kleine Kinder, die darf man nicht einfach so frei rumlaufen lassen, das ist nicht gut.
Die muss man irgendwo einsparren, wo sie dann in den Boden gepflegt werden, aber wo sich jemand drum kümmert.
Nein.
Die dürfen aber nicht raus.
Ja, genau.
Und das Ganze ist in so einem Kontextmanager drin und tatsächlich, also ich habe noch nicht so viel damit rumgespielt.
Also es sah gut aus, aber ja, angeblich ist das wohl eine Möglichkeit, das so hinzukriegen, dass man weiterhin lokal über Sachen nachdenken kann und dieses Problem, dass man Speichereffekte hat, gar nicht mehr bekommt.
Und es ist sogar teilweise einfacher, Sachen damit umzusetzen.
Es gibt ja diesen für IP Happy Eyeballs Algorithmus, der auch in Twisted drin ist.
Und es gibt eine Twisted-Implementierung, die ist 120 Zeilen und relativ fies.
Und es gibt eine in Trio und die ist halt irgendwie, weiß ich nicht, also ich meine, obwohl Zeilenvergleichen ist auch wieder ein bisschen Quatsch, aber also viel, viel weniger und viel klarer.
Und als er das Ding in Trio nachgeschrieben hat, hat er auch diverische Fehler in der Twisted-Implementation gefunden, weil das einfach super schwierig ist, das so hinzukriegen mit den Futures.
Also in Trio gibt es keine Futures mehr.
Es ist halt eher so, es gibt diese Nurse-Releys und ja, es wurde auch schon darüber nachgedacht, ob das nicht vielleicht eine gute Geschichte wäre, das in Python direkt einzubauen und statt Async.io zum Beispiel zu verwenden.
Aber das ist alles noch gerade so dabei, quasi noch nicht Spruch.
Ist der Zug nicht schon längst abgefahren?
Vielleicht schon, ja.
Ja, vielleicht ist er das schon, viel zu viele Leute oder viele Leute verwenden Async.io, als dass man das noch vielleicht ändern könnte und es gibt halt da diesen Ökosystem-Split und wahrscheinlich ist es besser, dann eben auf das zu versetzen, was eh dabei ist.
Trio macht das ja sowas, du hast jetzt gesagt, Kontextmanager, das heißt, man muss diesen lokalen Scope nicht verlassen.
Ist das nicht eigentlich das, was Funktionen tun?
Sowieso schon?
Ja, ja, aber Funktionen funktioniert halt nicht so richtig.
Weil?
Das laufen ja weiter.
Das ist halt auch bei, wenn du halt eine Funktion, wenn du Concurrence irgendwas machst, dann hört das halt nicht in dem Moment auf, wo du das aufgerufen hast, sondern das läuft halt unter Umständen weiter.
Das heißt, ich habe beispielsweise nur den halben Whisky eingeschenkt und die andere Hälfte kommt aber noch.
Ja, genau.
Aber woanders fängt er halt an, dann schon andere.
Dann passieren andere Dinge reinzuführen.
Und war es der Unterschied in Trio?
Da macht er dann trotzdem erst den einen Schritt fertig?
Oder wieso ist das so isoliert?
Also dass du vielleicht bei diesem Barkeeper-Beispiel das so anschaulich hinzubekommen?
Ich weiß nicht, ob ich das tatsächlich hinkriege.
Also muss man sich selber angucken.
Da gibt es auch Schaubilder auf der Seite, aber das zu erklären, weiß ich nicht, ob das ist.
Das ist so ein bisschen das Problem, das generelle Problem an Kurve Programming, oder?
Dass diese Sachen alle gleich so kompliziert werden, dass man dann diverse Schaubilder
braucht und Leute
dann irgendwann sagen, ja, lasst lieber die Finger davon, das ist nicht so.
Ja, und tatsächlich ist das vielleicht gar keine so schlecht.
Ich glaube, ich würde auch sagen, also auch meine Erfahrungen, die ich damit so bisher gesammelt habe, die waren alle immer, es war immer ziemlich fies.
Also das war immer.
Also die Beispiele, die haben immer funktioniert, wenn man sich irgendwie so Tutorials oder so anguckt.
Wenn man es dann tatsächlich ausprobiert, mit irgendwie in den Fällen, wo man es dann halt wirklich braucht, aber dann viel Last macht, dann sind mir jedes Mal passieren komische Sachen.
Und das Debugging ist jedes Mal.
Das ist so bei Programming, da passieren immer komische Sachen.
Ja, also wirklich, also zum Beispiel ein Bug, letzter, da habe ich da, da habe ich jetzt noch, und diese Geschichten, also ich meine, das passiert anderen Leuten auch, ja.
Also zum Beispiel der Gründer von der ursprünglichen Entwickler von Twisted hat einen langen Artikel darüber geschrieben, warum er Multithreading hasst, ja, wo er dann geschrieben hat, sie hatten also Probleme und sie hatten dieses, sie hatten einen wirklich fiesen Bug in einem größeren System und der ist halt irgendwie nur ab und zu aufgetreten.
Sie haben es überhaupt niemals reproduzieren können und sie sind es nicht losgeworden und sie haben alles, sie haben alles vom Scratch nochmal neu geschrieben, reimplementiert, um den Bug wegzukriegen, weil sie ihn einfach über Monate nicht finden konnten.
Und das ist eigentlich schon echt bitter.
Das ist ja auch super schwer zu testen, so Zeug.
Das ist ja auch super schwer zu sagen, hä, wenn ich X mache und Y, dann passiert Z, sondern es ist halt, wenn 37 Leute gleichzeitig X machen und einer von denen aber Z macht und einer von denen Y und 23 A und B, dann passiert irgendwas.
Buchstabensuppe.
Also das ist das Problem.
Partsiert Buchstabensuppe und das ist ein Bug.
Ja, was ich da mal hatte, war halt, genau, ich habe halt so Step-Crawler-Artiges geschrieben und mit ein paar tausend Seiten und weiß ich nicht, so 100 Seiten gleichzeitig abfragen pro Sekunde oder so, alles gar kein Problem.
Aber so, okay, jetzt sagen wir, gehen wir auf eine halbe Million oder eine Million und machen das mal so irgendwie ordentlich, so mit ein paar tausend pro Sekunde.
Und dann passieren plötzlich seltsame Geschichten.
Sterben plötzlich Sachen oder was ich dann hatte, ist, es wird plötzlich irgendwie seltsam langsamer.
Man weiß nicht warum.
Plötzlich kriegt man ganz eigenartige Tracebacks aus den Tiefen von Python.
Also was dann, was ich dann irgendwie rausgefallen.
Was ich dann rausgefunden hat, es war halt irgendwie dieser Resolver-Library auf meinem Mac, ich habe es noch Mac probiert, die hat halt irgendwie angefangen, plötzlich Probleme zu machen.
Und zum Glück auf Linux ging es dann.
Das ist auch immer sowas, ja, Mac gibt es dann vielleicht für Smoke-Tests gar nicht so schlecht.
Der macht dann irgendwie ein bisschen früher irgendwie perfekt da zu rauchen.
Dann weiß er schon.
Also bei Linux gehen dann viele Sachen, wo der Mac dann schon raucht.
Aber also das war, und da habe ich auch lange dran rumgedebuggt und nichts gefunden.
Und das war ganz schrecklich.
Also ja.
Ja, Load macht Dinge kaputt, die schwer zu finden sind und schwer zu sehen sind.
Und die dann auch nur so sporadisch auftauchen.
Da wird es dann auf einmal statistisch, wenn du sagst, okay, mein Programm geht in 99%
der Abläufe.
Das ist dann der echte Bug.
Das ist dann, dass in der Schaltung irgendwo ein Käfer reingeklettert ist, weil die Schaltung so groß ist, dass da irgendein Käfer dazwischen passt und du weißt aber nicht.
Aber wenn halt der
DNS-Resolver nur 99% aller Anfragen korrekt beantwortet und in einem Prozent der Anfragen halt abstürzt oder irgendein Quatsch macht, dann hast du halt so statistische Dinge.
Aber das ist natürlich sehr unbefriedigend, weil der eigentlich.
Also als Softwareentwickler oder als Informatiker ist man ja gewöhnt, dass wenn es geht, dass es dann zu 100% geht und immer.
Und nicht eben.
Oder ein anderes Beispiel, eben Lukas Langer, der hat ja bei Facebook gearbeitet und hat dann auch so ein Beispiel für.
Sie hatten halt irgendwie ein Ding, was halt auch Async-IO gemacht hat und dann haben sie da auch Durchsatz, wenn es anfing, so richtig Durchsatz zu machen, wurde es plötzlich irgendwie magisch langsamer.
Das sind die schlimmsten Sachen, ne?
Sie haben dann irgendwie auf ihre Kurven geguckt und sie haben es nicht verstanden und dann, bis dann irgendwann jemandem aufgefallen ist, oh, wo kommen eigentlich diese Kurven her?
Es gibt da halt noch einen Thread, der guckt sich halt an, was die anderen Threads so machen und was die anderen Tasts zu machen.
Und der hat halt in der Art, wie er dann halt die Ergebnisse von dem, was er gesehen hat, gesammelt hat, irgendwie so einen naiven Algorithmus drin, der mit der Last nicht gut klargekommen ist.
Der dann halt sehr, der dann halt schnell langsamer geworden ist und der wurde dann so langsam, dass er alles andere aufgehalten hat.
Und da ist dann sozusagen die CPU rausgelegt.
Ja, also, aber das ist alles so ganz ekelhafte Probleme.
Also das ist, wenn man das nicht braucht, sollte man das nicht machen, glaube ich.
Das ist ja so.
Ja, oder halt sich möglichst weiter von fernhalten.
Also ich meine, wir sind ja Django-Entwickler und wir haben das ja schon lange.
Wir haben ja schon lange Sachen, die concurrent sind, weil wir halt, wenn du irgendwo was deployst, dann machst du halt 10 Unicorn-Prozesse an oder so.
Aber die sind ja im Wesentlichen unabhängig voneinander.
Die kommunizieren nur über die Datenbank.
Die Datenbank ist da das, was die Concurrency macht oder was die machen.
Was die Wart-Probleme löst, dass das isoliert ist.
Genau, da bin ich ganz, ganz froh.
Ich einfach sagen kann, okay, das kann ich messen, wie lange braucht es, diese Query auszuführen und wie viele können das gleichzeitig und wenn da zu viele Leute gleichzeitig kommen, dann müssen die halt warten.
Und wenn dann zu viele da sind, dann muss man halt die Timerouts hochstellen.
So, fertig.
Aber das ist so ein bisschen Concurrency Light, ja, weil du eben die Sachen so sehr voneinander abtrennst.
Beziehungsweise das ist die smarte Concurrency.
Du trennst die Sachen so weit voneinander ab, dass sie unabhängig voneinander sind.
Und wenn sie so unabhängig voneinander sind, dass sie sich gar nicht, dass sie keine Kommunikation machen müssen, dann kannst du sie auf verschiedenen Rechnern ausführen.
Dann kannst du sie skalieren.
Kannst du sie wegskalieren.
Entweder über Prozesse oder über Prozessoren, über unterschiedliche Maschinen oder über was weiß ich, Docker-Container, die dir dann, oder über einen Kubernetes-Cluster, ja, wo du dann halt im Prinzip das Gleiche an ganz vielen verschiedenen Stellen ausführst.
Das ist ein Modell.
Ja, aber nehmen wir jetzt mal den Fall, du willst jetzt eben sowas machen wie zum Beispiel, keine Ahnung, was man mit Felix, Elixir und LiveView geht, halt, du hast halt eine Webseite und pushst den Leuten quasi reaktiv irgendwie Dinge in den Dom.
Und du hast dann natürlich eine Websocket Connection zu jedem Client und das können ja dann Tausende sein oder Zehntausende.
Dann musst du all diese Verbindungen handeln und musst damit was machen.
Wenn du jetzt dieses klassische Worker-Modell mit Unicorn machst, das geht auch, dann hast du aber irgendwie einen Haufen Verbindungen und brauchst einen Haufen Server.
Und wahrscheinlich kannst du es auch mit einem machen.
Ja, da würde ich mir einfach wünschen, dass es sozusagen so eine Verbindungsdatenbank gibt, die dieses schwierige Problem wegabstrahiert.
Aber ich glaube, da gibt es noch nichts.
Ja, aber tatsächlich mit Django 3.1 AsyncViews geht das tatsächlich.
Also ich meine, man kann auch einfach was anderes nehmen.
Also womit man das schon länger machen kann, ist wahrscheinlich sowas, wie man Starlet nimmt oder so oder irgendwas in der Richtung.
Damit geht das ja nicht.
Die machen ja auch alle AsynGaio, also die machen ja auch keine Magie.
Genau, genau, genau.
Aber da ging das halt.
Django ging es bisher halt noch nicht.
Also jetzt geht es halt dann halbwegs.
Was macht Django denn jetzt alles so Schönes?
Genau, jetzt kann es, also was neu dazu kommt, sind Async-Views, wo man halt sozusagen als View tatsächlich eine Funktion hat oder eben auch eine Klasse, wo man dann halt die Call-Methode als Async-Dev markiert, also Async-Funktionen hat, die Views sind und die dann Co-Routinen zurückgeben.
Und dafür muss das Interface, also das ist, was man schon mit Django 3.0 passiert, das Interface zum Server, also es muss möglich sein, was anderes zu benutzen als BSGI.
Und das, was man da verwendet, ist halt dann ASGI.
Und das kann halt beide Richtungen und ja, ist halt.
Also Webserver-Gateway-Interface ein asynchrones Server.
Genau.
Ist sozusagen das Übertragen auf den Fall, den man jetzt halt dann hat.
Und genau, kommt auch aus dem Channels-Projekt irgendwie.
Andrew Godwin hat das Ding halt auch den Standard irgendwie dafür sozusagen hauptsächlich, glaube ich, irgendwie geschrieben.
Ist jetzt in der Version 2.0 oder sowas da und ist auch mehr oder weniger fertig und wird auch von allen anderen quasi so adaptiert.
Also das hat sich wohl tatsächlich mittlerweile eigentlich als Standard für solche.
Hat sich durchgesetzt.
Hat sich durchgesetzt für, das ist halt der Standard, wie jetzt ein Applikations-Server mit der Applikation irgendwie kommuniziert.
Und genau.
Und was in Django 3.1 jetzt dazugekommen ist, sind die Views, das ging halt in 3.0 noch nicht.
Middlewares.
Und das war halt auch einer der schwierigen Teile halt.
Das Problem ist, du musst halt es hinkriegen, also wenn eine synchrone Middleware dazwischen ist, dann geht es halt schon eigentlich nicht mehr, die ganzen Middlewares async zu kriegen.
Und auch Async-Tests gehen halt auch.
Und damit ist halt tatsächlich schon mal was da, womit man was machen kann.
Ich meine, tatsächlich, also die Wunschvorstellung, die ich an das Ganze hätte, eigentlich ist da so, dass alle Dinge, die jetzt I.O.
machen auf meinem Server, also ich habe eine normale Webseite, kommt ein Request rein.
Das Ding macht halt irgendwie, keine Ahnung, fünf Statements an den Datenbank, fragt irgendwie ein Redis-Cache, fragt noch irgendeine API oder sowas und gibt dann irgendwie eine Antwort zurück in einem View.
Und momentan ist es halt so, da wartet man dann halt synchron auf jede einzelne Anfrage, die man irgendwo hinstellt und alle Latenzen addieren sich.
Und ja, bis man das erste Byte am Client empfängt, muss man halt diese ganzen Netzwerk-Zeiten abgewartet haben.
Und das kann halt sein, dass das dann irgendwann, wenn man viele Statements macht oder wenn viele Apis fragt, dann wird das halt viel.
Und man kann im Grunde nichts tun, so wirklich.
Oder man muss es halt komplett selber machen.
Das Framework gibt einem da nichts an die Hand, womit man das irgendwie verbessern kann.
Und sozusagen in einer goldenen Zukunft wäre es halt vielleicht so, dass man sagt, okay, das wird alles async zusammengesammelt.
Alle Sachen, die I.O.
machen nach außen, erwältigt man halt.
Und die Latenz, die man als Client, wenn man die Seite abfragt, zu sehen bekommt, ist die, die der längste I.O.-Ding nach draußen braucht.
Aber alle anderen sind dann halt schon da.
Das heißt, es ist nicht mehr, alle Latenzen addieren sich, sondern die Latenz, die man sieht, ist die von dem längsten Einzelteil sozusagen.
Ja.
Aber ist das jetzt nicht Parallelität?
Nö, das High Paper ist Concurrency.
Das ist alles immer nur.
Weil ich sehe da, ich sehe echt einen ganz anderen Anwendungsfall für diese Async-Views und Async-Middlewares und so weiter.
Ich sehe halt, dass du lange laufende Views hast und nicht den Worker blockierst.
Ja, das ist ja auch nicht.
Das heißt, du kannst derweil andere Clients behandeln.
Ja, ja, das gibt es natürlich nicht.
Und das ist ja genau das, was du bei Websockets brauchst.
Du hast ja im Wesentlichen einen Request, der für immer läuft.
Oder solange wie der Client halt da ist.
Und zwischendurch kannst du aber was anderes machen.
Und der kann prinzipiell ja dann auch mit anderen Views kommunizieren.
Du hast dich gerade freiwillig gemeldet, nochmal zu erklären, was Websockets sind.
Und weil das kam heute noch gar nicht dran.
Ja, Websockets sind, also da muss man ein kleines bisschen ausholen, glaube ich, weil das, was man so als Webseite kennt, ist, das hat so ein Modell, das heißt Request-Response.
Das heißt, du schickst eine Anfrage hin und der Server bearbeitet die und dann schickt er irgendwann eine Antwort zurück und dann ist das abgeschlossen.
Das ist alles, was da passiert.
Also das, was man da hinschickt, ist dann so eine Anfrage.
Entweder will man irgendwie aus dem klassischen Crub-Screen irgendwas machen.
Also lesen oder schreiben oder updaten oder löschen oder sowas und dann bekommst du eine Antwort, die dann irgendwie so einen Statuscode hat, die man vielleicht irgendwie so kennt.
Genau, das sind alles
Feinheiten, die da drin sind.
Dieser Request-Response-Zyklus ist einfach nur, ich schicke dir eine Anfrage hin, einen Arbeitsauftrag und der Server bearbeitet diesen Arbeitsauftrag und schickt dann das Ergebnis zurück.
Und was da drin steht, spielt erstmal keine Rolle.
Das kann sein, gib mir die Homepage, das kann auch sein, update alle Bilder in der Datenbank und es kann auch sein, lösche alle was weiß ich.
Das spielt erstmal keine Rolle, sondern das Wichtige ist, das ist eine abgeschlossene Welt.
Ich schicke eine Anfrage hin und die wird bearbeitet und dann wird sie zurückgeschickt.
Und wenn ich eine neue Anfrage hinschicke, dann hat die mit der vorherigen erstmal gar nichts zu tun.
Man muss dann Tricks benutzen, damit da sozusagen so eine Kohärenz da ist, dass ich der eingelockte Benutzer bin.
Aber im Endeffekt sind diese Tricks, um einzelne Request-Responses, um einzelne solche Zyklen so zu machen, dass die ausschauen, als ob sie zusammengehören.
Zum Beispiel sowas wie Session oder sowas.
Genau, das mit Cookies, wo du dann so eine Session-ID hast, dann kannst du simulieren, ja, dass diese verschiedenen Requests alle zusammengehören.
Aber im Endeffekt sind die alle unterschiedlich.
Die könnten alle einen anderen Web-Server gehen und es würde trotzdem funktionieren.
Und das ist in ganz vielen Fällen auch der Fall.
Wenn du bei Google die Hauptseite abrufst und danach die Suche abschickst, wirst du sicherlich nicht vom gleichen Rechner bedient werden.
Beziehungsweise du wirst eventuell sogar von ganz vielen Rechnern gleichzeitig bedient werden, weil die natürlich intern ganz viel Geschwindigkeit sagen.
Aber das spielt gar keine Rolle.
Für dich als Client, als Browser ist die Welt eine ganz einfache.
Du schickst eine Anfrage und dann kriegst du eine Antwort zurück.
Und dann ist das abgeschlossen.
Und wenn du danach was Neues brauchst, schickst du eine neue Anfrage, kriegst du eine neue Antwort zurück.
Aber bei WebSockets ist es anders.
Bei WebSockets machst du eine Verbindung auf und diese Verbindung bleibt dann bestehen.
Und du kannst dann über diese Verbindung Daten an den Server schicken.
Der Server kann aber auch selbstständig Daten an dich schicken.
Und das löst so ein Problem, was man früher had, wenn this classische Beispiel ist eine Chat-Anwendung, wo halt einer was in a chatfenster eintippt und die anderen sollen das dann sehen.
And by the request-Response-Mechanismus heißt es, dass die halt die ganze Zeit fragen müssen, gibt es schon was Neues, gibt es schon was Neues, gibt es schon was Neues?
And the server die ganze Zeit Nein sagt und irgendwann sagt er halt ja, weil da irgendjemand anders was eingetippt hat.
Viel besser wäre es aber doch, wenn die alle sagen, ich bin jetzt hier und ich möchte gerne alle Nachrichten aus dem Chatraum hören und sobald es eine neue Nachricht in diesem Chatraum gibt, sagt der Server allen angeschlossenen Clients, hier ist was tipped.
Dafür braucht man aber so eine bestehende Verbindung.
Das heißt, dafür kannst du nicht nur sagen, beantworte mal die Anfrage, gibt's was Neues, sondern da ist die Anfrage, informiere mich, sobald es was Neues gibt.
Man hat da früher ganz schlimme Technologien gemacht.
Man hat dann irgendwelche Long-Polling-Requests gemacht oder irgendwelche Meteor-Sachen, die die HTTP so ein bisschen verbogen haben, um das eben hinzukriegen.
Und hat dann irgendwann WebSocket zum Standard gemacht, was im Wesentlichen das macht.
Das ist eine HTTP, das ist erstmal eine normale HTTP-Anfrage, die dann upgraded wird, damit die Verbindung nicht geschlossen wird.
Und diese Verbindung bleibt dann bestehen und ist dann bidirektional.
Das heißt, der Client, der Browser kann Nachrichten checken.
Das muss nicht unbedingt ein Browser sein, kann auch was anderes sein.
Und der Server kann aber auch Nachrichten schicken.
Und dadurch wird eben so dieses Modell, dieses Programmiermodell, was man früher hatte, ein Request kommt rein, erzeugt eine Response, ausgehebelt.
Weil ein Request kommt rein, heißt, wir machen jetzt eine Verbindung auf und danach können sehr viele Responses darüber gehen.
Da können auch noch neue Requests drüber kommen, weil der Client halt, weil jemand was in sein Chat-Fenster eingetippt hat und der Client sagt dann, bitte veröffentliche das in diesem Kanal.
Das heißt, WebSockets sind so ein bisschen die Erweiterung, nicht eine Erweiterung, sondern eine Möglichkeit, rohere Netzwerkverbindungen zu haben, länger bestehende Netzwerkverbindungen zu haben aus normaler Browser-Software aus.
Es gibt inzwischen auch ganz viele andere Clients, die Websockets verwenden.
Die meisten Native-Anwendungen werden mit ihren Servern über WebSock kommunizieren, weil das eben der Standard ist.
Wie macht man sowas in Django?
In Django macht
man sowas erstmal gar nicht.
Beziehungsweise jetzt in 3.1 kann man das eventuell machen.
Die Websocket-Verbindung muss vom Client aufgebaut werden.
Das heißt, der Client muss sich irgendwo anmelden und sagen, hier, ich möchte gerne einen Websocket haben.
Und dafür gab es eben in Django diese Erweiterung, die Channels heißt, weil Django selber kann es nicht.
Django selber hat nur den Request-Response-Mechanismus.
Das ist sofort ersichtlich, wenn man die erste View-Funktion schreibt, weil die eben einen Request kriegt.
Und das ist genau der Request, der vom Browser geschickt wird, der sagt, mach mal bitte folgendes.
Und das, was eine View-Funktion erzeugt, ist dann eine Response und das ist genau der Abschluss dieses, sobald die View-Funktion fertig ist, ist die Verbindung weg.
Um da weiterzugehen, um da WebSockets machen zu können, braucht man eben was anderes und das war früher Channels.
Und jetzt wandert das so langsam in Django selbst rein.
Wobei, also das soll wohl nie tatsächlich nach Django reinkommen, sondern Channels wird jetzt halt das Ding zum Handeln von anderen Protokollen, die nicht HTTP sind.
Also zwar nicht nur DP.
Das ist dann
halt nicht, ja es sind halt Django Channels, sondern also es ist quasi.
Django Channels macht halt sowas dann wie Websockets, aber halt nicht nur das, sondern halt auch WebRTC und halt auch MQTT und so.
Ja, okay.
Okay, also alles Länge in Verbindungen braucht.
Genau, genau.
Aber du kannst es dann halt schon auch an Django damit anbinden, in gewisser Weise.
Genau.
Und es wird halt natürlich in Channels deutlich einfacher, weil wenn die Infrastruktur für ASGI halt schon in Django drin ist, dann brauchst du da nicht nochmal einen extra anderen Server oder so, sondern kannst du einfach so eine ganz normale Django-Infrastruktur weiterverwenden.
Ja.
Ja, ja, genau, genau.
Okay, also das ist das halt, was ich da als Anwendungsfall sehe, dass man da leichter sozusagen in diese Welt reinkommt.
Ja, also was ich total interessant finde, also wie gesagt nochmal, also wenn ihr das nicht kennt, Phoenix Elixir, würde ich mir Live View angucken.
Ja, das ist super spannend.
Was das Ding nämlich macht, ist, also man kennt das ja so, also ich würde sagen, das, was halt jetzt die meisten Leute schon kennen, Singlepage-App mit JavaScript, da funktioniert das halt so, du machst halt irgendwie auch mal GraphQL oder REST oder sonst was abfragen, irgendwie gegen ein Backend.
Und da gibt es auch teilweise sowas wie WebSockets oder so, aber im Grunde das ganze State-Handling deiner Applikation machst du halt im Client.
Beliebig eklig oder halt auch gut, keine Ahnung, Redux war eine Zeit lang innen, ist jetzt nicht mehr so, jetzt gibt es halt andere, die.
VX.
Ja und aber im Grunde, du machst das halt in der Applikation, weil du dann halt auch da das ganze Dom-Diffing machen kannst und dann halt nur die Sachen updaten, die wirklich geändert werden müssen und so.
Und damit das alles funktioniert, musst du das halt dann im Client machen und so.
Und dieses Phoenix macht halt, das macht das halt anders.
Das macht das alles auf dem Server.
Und das schickt sozusagen nicht irgendwie dir jetzt gedift irgendwie den DOM zurück, sondern es schickt dir halt sozusagen die Änderung an deinem State zurück an den Client und da wird es halt direkt im DOM geändert.
Aber dadurch, dass das halt sozusagen auf dem Server alles ausgerechnet wird, kann das sehr blöd sein auf dem Client.
Also da ist halt irgendwie der JavaScript halt von diesem Live-View-Dings ist halt irgendwie so tausend Zeilen.
Und ich habe einen Vortrag irgendwie von dem Entwickler davon gesehen, der meinte immer so, also ja, er muss immer aufpassen, er hatte manchmal das Gefühl, er ist jetzt hauptsächlich JavaScript-Entwickler und er muss er sich immer selber sagen, nein, ich fand diese JavaScript-Frameworks alle immer ganz furchtbar und ich wollte das anders machen.
Und ich sollte nicht jetzt anfangen, irgendwie das nächste verrückte JavaScript-Front-N zu schreiben, sondern das muss so wenig JavaScript bleiben wie möglich.
Und es ist halt auch relativ wenig, es sind halt irgendwie tausend Zeilen oder so.
Und alles andere passiert auf dem Server und das updatet den DOM direkt.
Also was über die Leitung geht, ist tatsächlich nur so etwas wie Name des Attributs von irgendeinem Formularfeld oder sowas, dann ein neuer Wert und dann wird es halt geändert und zwar direkt im Dom.
Und das ist halt natürlich nochmal deutlich schneller, als als man das normalerweise so gewöhnt ist von den JavaScript-Fremd-Frameworks und es ist halt weniger Kram auf der Leitung.
Was man normalerweise, was man selbst bei diesen Single-Page-Apps-Geschichten halt auf der Leitung hat, ist ja nicht nur der Payload, der übertragen wird von den APIs sozusagen, also weiß ich nicht, irgendwelches JSON, wo halt auch ganz viel immer dupliziert wird und ganz viel Boilerplate dabei ist, sondern was halt auch da immer noch mitkommt, ist halt diese ganze Protokollgeschichte und die Cookies, ja, so 4 Kilobyte Cookie-Kraben, der da hin und her geschickt wird.
Und es wird jedes Mal nochmal, auch der Server muss immer nochmal, achso, ach hier ist deine Signatur, ach, du bist der User, ach sehr gut.
Und alles klar, ich merke mir das mal und vergisse es sofort wieder beim nächsten Request.
Ja, das ist halt eigentlich, wenn man sich anguckt, was da tatsächlich passiert auf der Leitung, ist es alles ganz schön verrückt.
Und wenn man sich demgegenüber anguckt, was Elixir da macht, da geht einfach nur die rohen Daten, die gebraucht werden, um den DOM zu ändern, gehen über die Leitung und sonst nichts.
Und das ist wirklich super wenig.
Also das klingt ziemlich schlau.
Und der Witz ist, genau das gleiche könnte man mit Django eigentlich auch machen.
Du brauchst eigentlich auch nur diese Pause.
Das könnte man mal jemanden machen.
Ja, ich hatte auch schon so, ich dachte so, also vielleicht sollte man sich diese tausend Zeilen JavaScript da mal genau angucken und dann halt das Ganze mit Channels oder weiß ich nicht und Websockets machen, weil dass man den States mit Async und so super handeln kann, das kann man in Python und mit Django ganz genauso machen.
Das muss man nicht braucht man, das Erlangen da gar nicht.
Ja, also genau, das ist halt so ein bisschen das Versprechen.
Und das wäre natürlich schon sehr cool, wenn man sowas machen könnte.
Ja.
Genau.
Wir müssen noch ein paar Schlüsselwörter sagen, bevor wir bevor
wir aufhören können.
Soll ich mal etwas Wörter einwerfen?
Kooperatives Multitasking.
Ach ja, kooperatives Multitasking.
Genau, ja, wir haben jetzt noch gar nicht die wirklichen Unterschiede.
Also genau.
Es gibt jetzt unterschiedliche Ansätze, wie man diese Concurrency-Geschichte hinbekommt.
Ach, soll ich ein bisschen was über Geschichte erzählen?
Interessiert euch das?
19.
Jahrhundert, oder?
Also damals.
Also wie Concurrency irgendwie nach Python gekommen ist.
Ich habe das Großteil davon habe ich auch gesagt.
Märchen, was man seinen Kindern erzählt hat.
Wie Concurrency damals nach Python gekommen ist.
Ich kann nur diese Serie von Lukas Langer über AssynchIO empfehlen, da habe ich jetzt einen Teil von dem, was ich jetzt erzähle, her.
Und zwar ist es so irgendwie Mitte der 90er oder so, gab es da halt so erste Anwendungen für, man will vielleicht diverse Dinge parallel machen.
Ah, verdammt, jetzt bin ich selber die Falle getrachtet.
Concurrent machen.
Nebenläufig.
Nebenläufig, ja.
Und zwar, und da gab es das Medusa-Framework für.
Und das wurde zum Beispiel verwendet von Google, dann, um den Webcrawler zu bauen.
Und so ist halt Python ja auch irgendwie bei Google reingekommen, halt über den Crawler.
Und das ist auch irgendwann dann in die Standardbibliothek reingekommen als Asyncore.
Ich glaube, das Modul ist auch heute noch in der Standardbibliothek, aber ja, Deprecated.
Dann gab es halt eine ganze Zeit lang irgendwie so Ansätze, dann kam irgendwie, glaube ich, zur Jahrtausendwende irgendwie so um den Dreh irgendwie Twisted dazu.
Wurde dann auch viel verwendet, war aber immer inkompatibel zum Rest von Python, so die normalen Standardbibliothek-Funktionen, die blockieren alle, die machen alle Synchronen, I.O.
und so.
Das hat nie so richtig dazu gepasst.
Das heißt, das musste alles irgendwie anders gemacht werden.
Das heißt, man konnte Code, den man jetzt in Twisted, wo man Twisted benutzt hatte, um das Concurrent zu machen, nicht jetzt mit irgendwas anderem verwenden, wenn man jetzt vorhatte, auf G-Event umzusteigen oder so ging das halt nicht.
Ja, und aber das, aber Twisted ist auch ein Riesending.
Es gibt ganz viele Firmen, die das für irgendwelche Netzwerkgeschichten benutzt haben.
In Apple, Apple hat das nicht so.
Ja, auch super viele Sachen, die man vorher einfach nicht machen konnte.
Genau, genau.
Also ist das schon ein Riesending.
Der Mensch, der das entwickelt hat, Lewkowitz oder so, glaube ich, der hat auch dann den PEP zu Async.io mitgeschrieben.
Später, also Async.io ist dann ist auch sehr stark beeinflusst von Twisted.
Ein anderer starker Einfluss ist Tornado.
Auch einer aus dem Tornado-Projekt hat halt auch, einer von den drei Leuten, die halt den Async.io PEP mitgeschrieben haben, der andere ist dann Guido.
Und also Tornado und Twisted haben das beide stark beeinflusst.
Tornado ist auch eine ganz interessante Geschichte.
Es ist auch ein Ding, was Leute geschrieben haben, oder ich glaube, ursprünglich hat das mal Brad Taylor geschrieben.
Brad Taylor war bei Google, der hat irgendwie Gmail gemacht und Google Maps.
Und dann haben ein paar von den Leuten, auch Paul Buchhalt war, glaube ich, auch dabei.
Die haben dann eine neue Firma gemacht, irgendwann so 2007 oder so, Friendfeed.
Ich weiß nicht, ob ihr euch da noch dran erinnert.
Hast du mal gehört.
Ich hab's auch mal gehört, ich weiß nicht, ob ich es mal benutzt habe, keine Ahnung, ist dann von Facebook gekauft worden.
2009.
Und ja, Brad Taylor ist dann CTO von Facebook geworden.
Und ich hätte ja, ich hätte, das wird das letzte Zufällig, war auch, glaube ich, in der Podcast-Episode mit Lukas Langer, der war ja lange bei Facebook und erzählte dann so, dass intern bei Facebook ein großer Teil von Facebook ist halt irgendwie Python.
Fand ich überraschend, wusste ich gar nicht.
Und so komisch, wie kamen denn da Python rein?
Ja gut, vielleicht über Instagram oder so, aber nee, tatsächlich irgendwie, ja, der Autor von Tornado ist halt irgendwie, war halt CTO von Facebook lange Zeit.
Also ja, und hat da wahrscheinlich auch irgendwie seine Python-Vorlieben vielleicht so ein bisschen ausgelebt.
Jedenfalls, äh, genau.
Jeder bringt seine eigene Technologie mit.
Ja.
Genau, und ja.
Tornado ist auch heute noch irgendwie eine ganz interessante Geschichte.
Wird auch noch nach wie vor aktiv entwickelt.
Ist auch bei diesen Jupyter-Geschichten liegt das da irgendwie drunter.
Ja, und genau, aber das waren halt alles Dinge, die unabhängig voneinander halt existiert haben.
Und mit Python 2 war das sowieso alles nicht so gut kombinierbar.
Und dann, genau, ist halt 2012, glaube ich, gab es dann so erste Ansätze dazu, das mal dann tatsächlich in die Sprache in die Standardbibliothek und in die Sprache irgendwie reinzubringen.
Und ich glaube mit 3.4, das muss dann irgendwann so 2013 irgendwann gewesen sein.
Ich weiß es nicht so genau.
Ist dann halt auch sozusagen das in der Sprache gelandet, beziehungsweise in der Standardbibliothek mit diesen Dekoratoren für Async-Funktion.
Und dann in 3.5 kam die echte Sprachunterstützung mit Async Dev und Await dazu.
Also ab dann gibt es eigentlich tatsächlich richtig Unterstützung in der Syntax.
Ja, und es gab noch ein paar weitere Geschichten, die dann auch später dazugekommen sind.
Da hat sich auch noch einiges getan.
Also es ist heute viel angenehmer, das Ganze zu benutzen, also es ist viel angenehmer, das zu benutzen als früher.
Und es ist eigentlich inzwischen richtig klasse.
Also es ist aber was, ist eine völlig andere Geschichte als jetzt, es gibt jetzt auch andere Sachen.
Also man kann zum Beispiel, also Multithreading gab es in Python auch schon immer, war in Python 2 ganz, ganz schrecklich, weil noch schrecklicher.
Ja, weil es tatsächlich so ein Lock-Contension-Problem gab.
Also einmal ist es natürlich langsamer, als wenn es nur auf einem Thread ist, also je mehr Threads man hat, desto langsamer wird es eigentlich.
Und Python 2 ganz besonders schrecklich auf mehreren Prozessoren, weil da haben die Threads sich dann tatsächlich um den Gill gekloppt und ja, das Ganze wurde noch viel schlimmer, langsamer.
Aber auch da seit Python 3.2 gibt es einen neuen Jill.
Also in Python 2 wurde das so gemacht, oder beziehungsweise vor Python 3.2, dass der Interpreter alle 100 Ticks, also ein Tick ist halt irgendwie so eine, ja, ich weiß nicht.
Das ist ein Opcode, oder der aufgeführt wird.
Genau, das ist sozusagen eine primitive Operation, aber das kann auch sowas sein, also wenn man sagt, so vor I in Range eine Million, dann ist das auch nur ein Tick.
Das ist so ein bisschen schlecht, weil es kann halt sein, dass ein Tick sehr, sehr lange dauert.
Aber das Ding hat halt fix alle nach 100 Ticks irgendwie geguckt, okay, hat so eine Checkfunktion aufgerufen und die hat dann einmal Signal-Handling gemacht, aber auch geguckt, okay, welcher Thread läuft denn gerade?
Muss ich da irgendwas, muss ich vielleicht einen anderen Thread zu der in die Warteschlange der Threads, die jetzt runnable sind, fürs Betriebssystem reintun oder nicht?
Das ist uns auch so aus, Python-Thread sind echte Betriebssystem-Sthreads, also was ich dann oft lese, irgendwie sowas wie, ja, der Python-Interpreter scheduled da irgendwas.
Nee, macht er nicht, das macht das Betriebssystem.
Aber der Python-Interpreter kann natürlich sagen, okay, also dieser Thread hier wäre jetzt theoretisch runnable.
Wer dann vom Betriebssystem tatsächlich sozusagen, wo der Kontext-Switch hingeht und welcher dann weiter ausgeführt wird, das macht das Betriebssystem dann.
Wie nennt man das, Jochen?
Jetzt muss ich kurz das Schlüsselwort dazwischen sagen.
Präemptives Multitasking.
Genau, und das ist das
abgebrochen werden kann.
Präemptives Multitasking, weil man selber sozusagen, wenn man jetzt in einem Thread ist, kann man nicht bestimmen, wann einem die Kontrolle entzogen wird oder weil man es jeder bekommt.
Und es kann jederzeit passieren.
Ich habe mich da mal mit jemandem drüber gestritten und musste dann leider mir zeigen lassen, dass ich da Unrecht hatte, das kann auch innerhalb eines Python-Befehls sein.
Also Python-Befehle selbst sind da nicht atomar, sondern es sind eben diese Bytecodes, die da drunter liegen, die atomar sind.
Aber so eine Zuweisung wie i plus gleich 1 ist auch nicht atomar, sondern das ist, kann zwischendurch unterbrochen werden.
Und genau das ist da die große Gefahr daran, ja, dass man eben innerhalb eines Befehls, den man selbst als unteilbar sieht.
Ja, kann man irgendwie die Kontrolle weggenommen kriegen, es passiert irgendwas anderes, jemand anders ändert irgendeinen Wert und man selber fängt wieder an und plötzlich sind die Sachen anders, als man erwartet hätte und es passieren schreckliche Dinge, ja.
Also das ist präventives Multitasking.
Präventives Multitasking, ja, genau.
Hat natürlich gewisse Vorteile, aber es ist halt unter, also ja, genau, aber erstmal um das so zu, genau, das ist Präventives und das, was man aber heute hauptsächlich macht und was halt auch eine gewisse Geschichte, also auch schon immer eine Geschichte hatte, auch sind halt diese sozusagen Dinge, die man innerhalb von dem Thread oder innerhalb von einem Prozess ausführt.
Also wo das Betriebssystem gar nichts drüber weiß, dass man das macht.
Wo man das halt selber sozusagen entscheidet, dass man jetzt die Kontrolle abgibt oder was anderes macht.
Und das ist dann halt kooperatives Multitasking.
Und das ist halt so das, was man kennt aus, also in Java heißen die Dinger dann Green Threads oder das ist halt auch das, was in Stackless-Pfalten passiert ist oder das, was G-Event gemacht hat.
Eventlets oder so, oder das gibt es.
Ja, genau, Eventlets.
LibEV liegt da, glaube ich, zum Beispiel unter Umständen drunter.
Oder auch LibUV, also die Event Loop von Node.js.
Das ist LibEV, hat glaube ich Armin Rigo geschrieben.
Was ich auch faszinierend fand, die gleichen Namen, die man, also manchmal, da tauchten viele Namen auf, die ich schon kannte.
Die sind erstaunlich wenige Leute, kann die machen.
Das sind erstaunlich wenige Leute, ich kannte die aus anderen Teilen schon und dachte so, ach, diese haben auch diese ganzen Geschichten da, da haben die auch alle irgendwie mitgemacht, das wusste ich gar nicht.
Also genau, Armin Rigo habe ich nur gehört im Zusammenhang mit PyPI, aber nie mit ähnlich PyPy, aber nie mit irgendwie so Concurrency-Geschichten, aber ja, der hat, glaube ich, eine Bibliothek geschrieben, GreenLad, die auf Lib EV basiert.
Und darauf basiert wieder G-Event.
Und G-Event macht halt quasi sozusagen User Space irgendwie Threads und macht da aber auch, teilt das irgendwie so halbwegs zufällig ein, wann die laufen und wann die nicht laufen.
Und damit das halt mit dem Rest von der synchronen und blockierenden IO-Funktionen und so im Rest von Fighten funktioniert, Monkey-patchen sie einfach alles.
So zum Beispiel das Socket-Modul ganz böse.
Das ist aber auch hart.
Das ist ziemlich hart.
Sieht das jetzt so, okay, ja, das geht natürlich auch.
Das funktioniert.
Wir gehen jetzt erstmal überall rein und.
Ja, also das ist schon bitter.
Aber muss ich auch nicht, zum Beispiel G-Event ist halt das, was auch unter Gunicorn liegt.
Deswegen heißt das Ding G Unicorn.
Deshalb heißt es G-Unicorn.
Und genau.
Gab's nicht sogar früher mal was, das hieß Green Unicorn und es ist dann so G-Unicorn geworden.
Ja, genau.
Und genau, das ist halt alles sozusagen kooperatives Multitasking im Weiteren.
Wobei, weiß ich jetzt gar nicht, bei G-Event bin ich mir jetzt gar nicht so super sicher.
Klar, bei G-Event ist es auch kooperativ.
Ja, also im Prinzip ist es kooperativer.
Im Wesentlichen jetzt ist ja AsynGaio oder so der Event.
Genau, das ist jetzt sozusagen der heißeste Scheiß, den man jetzt da macht.
Und ehrlich gesagt, also ich habe ja jetzt da jetzt auch eine ganze Zeit lang Sachen angehört, das sieht auch deutlich besser aus als alles, was man da so vorher gemacht hat.
Aber im Wesentlichen heißt es doch jedes Mal, wenn du Yield sagst, wird dir die Kontrolle erstmal weggenommen.
Yield?
Ja, nicht Yield, äh, Await.
Ah, wenn du Await sagst, okay.
Ja, früher war es Yield.
Ja, Yield from, genau, das war doch die Sonntags davor.
Aber das ist so ein bisschen der zentrale Mechanismus, oder?
Dass du sagst, okay, ich mache jetzt was und sobald ich Await sage, kann jemand anders was machen.
Genau, genau.
Und das hat eine interessante Auswirkung auf die Art oder auf die Schwierigkeit, also wie schwer das zu programmieren ist, weil wenn du präventives Multitasking hast und an jeder Stelle kann im Grunde die Kontrolle weggehen, dann alle Dinge, die irgendwie atomar sein müssen, musst du halt dann mit Loks absichern.
Genau, und das Problem ist halt, du musst darfst halt nichts vergessen und es kann halt überall passieren.
Und du musst halt, also wenn.
Und selbst wenn du nichts, wenn du nichts vergessen hast, kommst du immer noch in so Deadlock-Situationen rein oder in so Starving-Situationen.
Selbst wenn du alles richtig machst, kann es immer noch.
Eine Starving-Situation ist, es wartet irgendjemand darauf, dass irgendwie wer losrennen kann, aber da kommt einfach nichts.
Nee, das ist ein Deadlock.
Deadlock heißt, zwei oder mehr Dinge warten auf sich gegenseitig.
Und ein Starving.
Dann kann keiner loslaufen.
Und Starving heißt, jemand, der etwas tun könnte, kommt nie dran.
Weil immer jemand anders schon was tut.
Das heißt, dass der nicht selber gelockt ist, sondern dass er einfach nicht
immer einer voraspekt
ist oder weil er immer sagt, macht ihr erstmal, dass der sozusagen für immer wartet, obwohl er etwas tun könnte, obwohl er nicht irgendwo.
Ach nö, wäre ja so schön, aber die können das ja alle schon so gut ich bleib dann mal hier stehen.
Genau, und sowas passiert dann halt.
So was passiert halt in so präemptiven Sachen, weil du keine Kontrolle drüber hast, wer läuft.
Beziehungsweise die Kontrolle, die, die jemand hat, ist eben das Betriebssystem.
Und das muss dafür sorgen, dass so ein Deadlock entweder aufgelöst wird oder dass solche Starving-Situationen nicht passieren.
Ja, also es gibt halt diverse Techniken, mit denen man das halt auch, also die Probleme minimieren kann.
Es gibt dann so ein paar Patterns und wahrscheinlich würde ich auch sagen, also für die meisten Fälle nimmt man halt so ein Pattern und dann kann eigentlich nicht mehr so wahnsinnig viel passieren.
Man nimmt halt eine Queue oder halt viele Ques und macht da halt Loks rein und da gibt es auch schon fertige Dinge, wo man die Loks nicht selber setzen muss.
Und dann ist es.
Ja, auch da ist es erstaunlich leicht, in so Situationen reinzukommen, wo du dich deadlogst.
Ja, also natürlich.
Es ist erstaunlich leicht.
Ja, ja, ich würde auch sagen, also es ist halt sehr scharfkartige Geschichte.
Ja, man muss halt echt aufpassen.
Und genau, achso, richtig, genau.
Seit, das habe ich eben nicht mehr erzählt, genau, nach 100 Ticks immer diese Geschichten, aber das hat halt diverse Nachteile, weil Tick ist halt, kann halt sehr lange sein.
Und dann gab es halt auch diese Battles über werkrächtigen Gill quasi auf unterschiedlichen Prozessoren.
Und da so war halt, das war wie David Beasley, da gibt es halt Understanding the Jill zum Beispiel den Vortrag von 2014 oder Verhalten auf mehreren.
Also er sagte irgendwie auf einer, also mit einem Sri, also alles gut, mit mehreren Stres, na nicht so toll, auf mehreren Prozessoren diabolical.
Das ist wirklich nicht schön.
Und ja, das ist aber mit Python 3.2 besser geworden.
Da gab es einen neuen Jill und der macht das nicht mehr nach Ticks, sondern es gibt jetzt einen Zeitintervall, was halt eben dazu führt, dass halt nicht quasi Dinge für immer warten müssen, weil halt ein Tick halt super lang war.
Und zwar jetzt ist es halt so, jetzt ist es nicht alle 100 Ticks, sondern jetzt ist alle 5 Millisekunden.
Man kann das aber auch einstellen, wie man möchte.
Also das konnte man vorher auch schon.
So alle fünf Millisekunden guckt halt der Interpreter nach.
So, muss man jemand anders dran.
Bitte?
Muss man jemand anders dran sehen?
Ja, genau, kann man jemand anders dran oder welche Threads können dann laufen?
Wobei letztlich wäre das entscheidet, ist das Betriebssystem.
Ja, und das funktioniert jetzt deutlich besser.
Das funktioniert auf mehreren CPUs auch gut.
Ja, es ist nicht so, dass jetzt da auf mehreren CPUs gleichzeitig was gemacht würde, aber es ist nicht mehr so, dass die Performance viel schlechter ist, als hätte man nur eine CPU hätte, was ja schon mal auch nicht so schlecht ist.
Genau.
Daher, also Threading kann man auch verwenden.
Also es ist auch nicht so, also es funktioniert mittlerweile alles ganz gut auf Linux, also auch 10.000 Threads aufmachen.
Gar kein Problem, funktioniert.
Neulich hast du ausprobiert, Jochen.
Bei 9500 war Schluss bei mir.
Aber also das geht alles.
Und das ist auch nicht, so das findet man auch oft, ich war überrascht, ich habe da so ein bisschen recherchiert, wie oft da so Dinge auch in Büchern drin stehen.
Ich habe mir zum Beispiel Fluent Python mir angeguckt oder auch Effective Python von Brad Sladkin, also eigentlich beides sehr renommierte Quellen, weil ich was so, okay, so die Standardwerke, da muss es doch drinstehen, wie es geht.
Und die sagen beide irgendwie, ah, Sweats ganz schlecht, du kannst irgendwie nicht so viele Threads aufmachen, weil der Stackspace-Betriebssystem riesig, brauchst du halt 8 MB, zum Beispiel sagt Effective Python.
Bei Fluid Python ist es halt auch immer in Größenordnung von jeder Thread braucht so Größenordnung MB, aber das ist halt nur Virtual Memory.
Das ist gar nicht Resident Memory.
Das heißt, das stimmt so nicht.
Das ist viel weniger.
Und das Kontext-Switchen, also es gibt da auch so einen schönen Artikel, wo jemand das alles mal gemessen hat und das ist eigentlich sollte das alles kein Problem sein.
Und vielleicht war das früher auch ein Problem, weil auf 32-Bit ist es tatsächlich blöd, weil der Rest-Space nicht so groß ist, aber 64-Bit, ja, sollte alles kein Ding sein.
Und ja, Kontext-Switchen ist auch so schnell, das sollte auch kein Problem sein.
Es könnte immer noch diese Log-Contention-Probleme geben, aber ich weiß nicht so genau.
Naja, und achso, was auch so ist, der Jill wird immer automatisch abgegeben, wenn irgendwie so Netzwerk-Blocking-Funktionen aufgerufen werden, dann ist der auch sofort immer weg.
Das heißt, das passiert, selbst wenn man ein pre-bus-Multitasking hat, was man möchte.
Genau, passiert das automatisch.
Also da muss man sich auch nicht selber darum kümmern, sondern da gibt der Thread, der halt irgendeinen blockierenden Netzwerkaufruf gemacht hat oder IO-Aufruf, da ist der Gill sofort weg.
Und ja.
Also ich würde sagen, mit Threads kann man eigentlich auch alle Sachen so, also wenn man es darum geht, IO zu multiplexen, das kann man mit Threads auch wunderbar machen.
Es ist schwerer zu programmieren, es ist halt ätzend, dass man nicht weiß, wann die Kontrolle weggenommen wird und so.
Es ist im Debugging.
Im Debugging ist ziemlich übel, ja.
Probleme, die wir vorhin erzählt haben, da kommen dann halt irgendwelche Fehlermeldungen von irgendwo her.
Irgendein Callback hat mal kurz gesagt.
Hallo, ich sterbe.
Dann steht man halt da.
Okay.
Aber es geht auch.
Und ich würde jetzt nicht sagen, das kann man gar nicht machen, sondern das geht auch.
Aber tatsächlich, also in I.O.
würde ich auch sagen mittlerweile deutlich besser, weil das ist jetzt kooperativ, da ist es so, wenn man, da dreht sozusagen die Geschichte um.
Bei Threads ist es so, man kann überall die Kontrolle weggenommen kriegen und Dinge sind bedroht, solange man nicht gesagt hat, okay, ich locke das jetzt hier.
Ich benutze jetzt irgendeine Kernel-Datenstruktur, um zu sagen, so, da bitte nicht, so, dann ist das geschützt, aber alles andere ist halt irgendwie wilde, wilder Westen, Drachen kommen vorbei, verbrennen an die Hütte.
Also ist halt, ja, weiß man nicht.
Und bei Async.io ist es quasi umgekehrt, da gibt es halt, also die Stellen, an denen böse Dinge passieren können, die sieht man, die sind markiert.
Da steht Await vor.
Und alle anderen Sachen, der ganze andere Code, das ist einfach ganz normaler Code, der so durchläuft, wo man sich sicher sein kann, dass einem die Kontrolle da nicht weggenommen wird.
Und da kann man auch Operationen machen und wenn da nur, wenn da keiner wegkommt, dann ist das alles atomar, weil kann sonst ja gar nichts passieren.
Und das ist natürlich schön, weil die Anzahl möglicher Fehlerquellen wird halt deutlich geringer.
Aber man bezahlt natürlich in gewisser Weise einen Preis dafür und der ist halt, naja, also man muss sich halt auch selber darum kümmern, dass das halt, dass man die Kontrolle abgibt, wenn es geht, weil man alle anderen blockiert, wenn man das nicht tut.
Also wenn man jetzt irgendwas.
Nein, man muss einfach kooperativ sein.
Man muss kooperativ sein.
Wenn man irgendwas macht, was ein paar lange dauert, dann gehen halt alle anderen kaputt, was halt auch unter Umständen natürlich servose Konsequenzen haben kann.
Ja.
Aber da sieht man dann wenigstens.
Also ich meine, wenn du da dann den Interpreter unterbrichst, weißt du, siehst du, wo du bist.
Siehst du,
wer schuld ist.
Genau, es ist auch so, dass man, ich glaube Python-X, wenn man Debug anschaltet und Trace Melloc anschaltet, das sollte man immer machen, wenn man irgendwie so Asyn-IO-Sachen, zumindest auf der Entwicklungsseite macht, dann sollte man das immer anschalten.
Produktion natürlich unterwahrscheinlich nicht, kostet auch Ressourcen, aber dann sagt einem der Interpreter zum Beispiel sowas wie, also hier hast du irgendwie einen Task oder was, diese Funktion hockt die CPU und zwar ganz übel, weil der das intern halt aufruft mit einem Timeout und das halt mitkriegt und dann die Exception fängt, wenn das halt zu lange dauert.
Und er sagt halt so, also diese Funktion da, die macht irgendwas Böses, das sieht man dann tatsächlich in der Ausgabe oder sagt einem auch, wenn man Sachen nicht awaited hat und so.
Type-Annotations auch total sinnvoll, vielleicht bei so hakeligen Geschichten.
Da kann einem der Interpreter auch noch eine Menge sagen, was da halt dann schief geht.
Weil er halt weiß, dass da jetzt eigentlich eine Coroutine zurückkommen sollte und so.
Und wenn das halt nicht passiert und dann, ja.
Also da geht einiges.
Und es ist halt, wie gesagt, also die bösen Stellen sind hier, wo Avater vorsteht und alles andere, ist halt sozusagen ein ganz normaler Python-Code eigentlich.
Ja.
Und das ist halt schon schön.
Und von den Ressourcengeschichten würde ich jetzt zwar sagen, nachdem ich mich so ein bisschen damit beschäftigt habe, es ist nicht so, also es ist, ich weiß, es gibt Hitness-Schwarts auch, aber natürlich ist es auch so, dass die sind schon ressourcenaufwendiger.
Also das habe ich jetzt nicht nachvollzogen, aber Koroutine ist halt eigentlich nur ein Funktionsaufruf und braucht halt einen Kilobyte-Haubspeicher oder so und davon halt ganz, ganz viel zu haben, ist im Grunde überhaupt kein Problem.
Du kannst zehn, Hunderttausende davon haben, kein Ding.
Du musst halt bloß schnell genug bleiben, dass du halt tatsächlich, dass diese hunderttausend Sachen auch irgendwann alle mal wieder drankommen, aber ansonsten geht das.
Und ja, da gibt es ja dann auch sehr schnelle Event Loops drunter.
Also man kann halt auch, es gibt die Referenzimplementation der Event Loop in Python, die ist nicht schnell, aber die ist halt dafür da, damit man weiß, wie es funktioniert.
Wenn man jetzt eine andere Implementation schreibt, die auch dagegen testen kann und zu gucken, verhält die sich jetzt genauso, wie sie sollte und so.
Für Produktionen kann man dann halt sowas nehmen wie UV, diese UV-Loop, also Lib UV.
Und das ist die Node.js Event Loop und die ist halt echt sackschnell.
Also ja, und damit kriegt man halt unter Python quasi da auch dann die gleiche Geschwindigkeit hin.
Ja.
Haben wir erklärt, was eine Event Loop ist?
Nein.
Ich hätte jetzt mal einen Vorschlag.
Ich würde jetzt auch so langsam mal die Kontrolle abgeben.
Ja, ja, ja.
Kann mal A Wave machen.
Yield.
Genau, ich würde einmal Yield sagen und dann direkt Sleep aufrufen.
Ja, ja, ja.
Das ist doch ein sehr schönes Stichwort, oder?
Ja.
Aber es hört sich für mich so an, als ob wir noch mindestens zwei, drei Episoden daraus machen müssen.
Vielleicht machen wir einfach noch eine.
Alle Worte gesagt haben, die gesagt werden müssen.
Also mir fallen noch viele Worte ein.
Mir fallen sicherlich auch noch ganz viele Worte ein.
Es ist auch, wenn ich mich so an diese Vorlesungen erinnere, die wir da gemacht haben, es hat eine gewisse intellektuelle Schönheit, wenn man diese ganzen Locking-Algorithmen dann hat, ja.
Wenn man die Dining Philosophers einmal besiegt hat oder wenn man einmal die Lockless Data Structures alle erfunden hat.
Das hat auch was Schönes, aber es ist nicht was, was man jeden Tag in seiner Arbeit machen möchte,
glaube ich.
Nee, es ist wirklich schwer.
Es ist auch eine massive Fehlerquelle.
Ja, ja.
Es ist hart.
Also das ist schon mal.
Mein Fuß brennt immer noch von so einem Swedishing Vorfall.
Ja, also es ist einfach sehr leicht, damit Fehler zu machen.
Und ich glaube, dass das einer der großen Vorteile von diesen ganzen Asyncai-Ursachen jetzt ist, wie du eben gesagt hast, der Code ist einfach linear und nur wenn ich Await sage, kann irgendwas Schlimmes passieren.
Ich bin in der Verantwortung, dass ich Await sagen muss, damit ich kooperativ bin, damit die anderen auch mal können.
Aber solange ich das nicht sage, passiert mir nichts.
Und das nimmt ganz, ganz, ganz viele von diesen Ecken und Kanten heraus, die einem sonst eben ganz schlimm da reinfahren, die ehrlich die kritischen Sektionen kaputt machen.
Und das ist eine sehr gute Entwicklung.
Ich begrüße
das ja.
Und das war ein sehr schönes Schlusswort, lieber Johannes.
Alles klar, dann.
Als ob ich es geplant hätte.
Ja, würde ich sagen, machen wir doch einfach vielleicht dann.
Ja, an dieser Stelle weiter.
Werd uns gewogen, wann immer ihr uns hört, lieber gerne, immer weiter.
Abends, morgens, mittags, nachts, an welchem Erdballteil ihr euch gerade befindet.
Oder irgendwo out of Space, man weiß es immer nicht genau.
Ja, schön, dass ihr wieder eingeschaltet habt.
Alle Kritik, Feedback und so weiter.
Hallo.Pythes-Podcast.de.
Bis zum nächsten Mal.
Tschüss.
Hallo, bis dann.
Tschüss.