Seite 1 von 2
queue+threads
Verfasst: Samstag 14. August 2021, 15:26
von frank-w
Hi,
Ich lese mich gerade in queues ein. Dabei ist mir aufgefallen,dass in Verbindung mit Threads (fast) immer eine Endlosschleife verwendet wird:
https://pymotw.com/2/Queue/
Eigentlich wäre das logischer für mich:
Bei der Endlosschleife würde ich davon ausgehen,dass irgendwann die empty exception geworfen wird.
Vielleicht kann mit das jemand erklären
Gruß Frank
Re: queue+threads
Verfasst: Samstag 14. August 2021, 17:53
von Sirius3
Es wird keine Exception geworfen, weil get so lange blockiert, bis wieder was in der Queue ist. Sinn solcher Threads ist es, das sie Aufgaben abarbeiten, die z.b. von Nutzern der GUI ausgelöst werden.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 09:03
von frank-w
Danke dir, habe das mit der python doku [1] nachvollziehen können (block=True).
D.h. der thread läuft die ganze Zeit,bis der Prozess (Anwendung) beendet wird. Ein daemon=True wäre hier fatal,richtig?
Kann man den Thread dann irgendwie beenden? Muss vermutlich in der klasse eine Abbruchbedingung setzen,welche innerhalb der "while true" schleife geprüft wird (setzt natürlich vorraus,dass die position noch erreicht wird,da evtl.bei get hängt)...ein thread.stop habe ich noch nicht gefunden.evtl.ist der sauberste Weg,die "Abbruchbedingung" auch in die queue zu schicken

dann wird get getriggert und kann die Schleife verlassen und den thread beenden.
[1]
https://docs.python.org/3/library/queue.html
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 10:22
von __blackjack__
@frank-w: Warum sollte ein ``daemon=True`` fatal sein? Das sollte man eigentlich fast immer machen. Es gibt eher wenige Fälle wo man das nicht machen möchte, weil in den meisten Fällen es einfach nur nervig ist wenn Prozesse von aussen gekillt werden müssen weil das Hauptprogramm zwar eigentlich beendet werden sollte, es aber Threads gibt, die das Ende verhindern.
Abbruchbedingung über die Queue wenn da noch irgendwie auf den Abbruch reagiert werden soll, bevor es tatsächlich abbricht. Ansonsten reicht ``daemon=True`` und beenden des Hauptprogramms.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 12:55
von frank-w
Daemon=true sorgt ja dafür (wenn ich es richtig verstehe) dass der thread weiterläuft,wenn das hauptprogramm beendet wird,bis der Thread fertig ist. Wenn ich while true mache und queue.get auf weitere daten wartet,läuft der Thread doch ewig weiter,oder nicht? Also grob das Szenario,was du ansprichst...
Oder wird die queue dann zerstört und damit der Thread beendet?
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 12:58
von __blackjack__
@frank-w Du hast `daemon` genau falsch herum verstanden. Wenn man das auf `True` setzt, dann läuft der Thread gerade *nicht* weiter wenn das Hauptprogramm beendet wird, sondern wird mit dem Hauptprogramm beendet.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 13:13
von __deets__
__blackjack__ hat natuerlich recht, ich finde den Begriff und die Semantik aber tatsaechlich auch verwirrend. Ein Daemon laeuft an sich ja unabhaengig von anderen Systemteilen, aber hier dann nicht, sondern er wird eben gestoppt. Wohingegen er munter weiterlaeuft, wenn daemon=False... finde ich ungluecklich.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 17:24
von frank-w
Danke euch...habe es wirklich andersherum (vom losgelösten prozess unter linux) verstanden...die doku ist da ein bisschen verwirrend (doppelte Verneinung)
Ich habe es jetzt so (self.workthread wird im init der Klasse auf None gesetzt):
Code: Alles auswählen
def worker(self):
while True:
item = self.q.get()
self.scrolltxt.insert(tk.END, str(item)+ '\n')
self.q.task_done()
sleep(1)
def createthread(self):
if not self.workthread or not self.workthread.is_alive():
self.workthread=Thread(target=self.worker,daemon=True)
self.workthread.start()
def clickme(self,num=0):
c1=num
c2=num+30
for item in range(c1,c2):
self.q.put(item)
self.createthread()
Ist natürlich exemplarisch...es füllt die queue einmal vor dem start des threads und beim zweiten klick ist der thread noch aktiv (wird nicht nochmal gestartet) und da wird nur die queue gefüllt
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 18:20
von frank-w
Wie kann ich den thread als gelöst kennzeichnen? Habs bisher nicht gefunden...
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 18:24
von rogerb
@frank-w,
Das gibt's auch nicht. Hier wird nur diskutiert und nichts gelöst

Re: queue+threads
Verfasst: Sonntag 15. August 2021, 18:42
von __blackjack__
@frank-w: Der Code wäre ein bisschen einfacher wenn Du den Thread schon in der `__init__()` erstellst und startest. Solange der in `get()` hängt, verbraucht der keine Rechenzeit.
Edit: Argh: Gerade gesehen: Der Thread ändert was in der GUI — das ist falsch. Die darfst Du nur von dem Thread aus verändern, in dem die GUI-Hauptschleife läuft.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 18:51
von frank-w
Das Ändern in der gui vom thread aus habe ich grob aus einem Beispiel (buch)...wie bekommt die gui mit,dass der thread was gemacht hat,ohne zu blockieren? Über eine 2.queue (get würde halt die gui blockieren,genauso wie eine schleife zum pollen dieser)?
Betreffend der gelöst-Kennzeichnung habe ich die grünen Haken bei den anderen Themen gesehen und wollte mich dran halten,da es in anderen Foren zum "guten Ton" gehört
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 19:45
von __blackjack__
@frank-w: Man pollt üblicherweise eine Queue mit einer Methode und `Widget.after()`.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 19:58
von frank-w
Eine bekannte Suchmaschine hat das zu Tage gefördert:
https://stackoverflow.com/q/49370592
Wenn ich pause_and_empty nach dem Füllen der queue aufrufe, müsste das passen,oder? Brauche ich das pause oder reicht das Abfragen,ob die queue leer ist? self.parent muss dann vermutlich der button sein,der die queue gefüllt hat,oder?
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 21:18
von __blackjack__
@frank-w: `self.parent` muss *irgendein* `Widget` sein. Welche ist egal. Diese `pause_and_empty()`-Methode brauchst Du IMHO überhaupt nicht. Der Code da ist komisch und unübersichtlich und die Frage ist mit -1 bewertet und es gibt auch keine richtige Antwort.
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 21:23
von frank-w
Kannst du mir vielleicht ein besseres beispiel geben? Der sinn an der funktion ist ja die rekursion...also wenn bei einer prüfung noch was in der queue drin ist,wird nochmal gewartet.
Kann auch das Fenster selbst verwendet werden?
Hier wird das scheinbar gemacht:
https://www.oreilly.com/library/view/py ... 09s07.html
Auch wenn ich das mit dem sys.exit nicht ganz nachvollziehen kann...self.running wird ja nur in endApplication auf 0 gesetzt,welches nie aufgerufen wird
Re: queue+threads
Verfasst: Sonntag 15. August 2021, 22:10
von __blackjack__
Ja das mit dem `exit()` ist schräg, da würde man einfach per `quit()` auf dem passenden Objekt die `mainloop()` verlassen.
Es wird nie gewartet, also nicht in `Queue.get()`. Und es wird immer `after()` aufgerufen. Es sei denn Du definierst irgendeinen Wert den der/die Threads an das Hauptprogramm/die GUI senden können/dürfen, der das Programm beenden soll.
Rekursion ist da übrigens keine. Die Methode ruft `after()` auf und kehrt zur Hauptschleife zurück. Das *die* dann später wieder die Methode aufruft ist keine Rekursion. Dafür müsste die Methode gleichzeitig mehrfach aktiv sein.
Re: queue+threads
Verfasst: Montag 16. August 2021, 09:16
von frank-w
Habs jetzt mit einer 2.queue (Rückrichtung als Erledigungsmeldung) und ohne exit/quit (macht aus meiner sicht keinen Sinn,da die job-queue mehrmals gefüllt werden kann) gemacht.
Code: Alles auswählen
def syncGUI(self):
while not self.doneq.empty():
item=self.doneq.get()
self.scrolltxt.insert(tk.END, str(item)+ '\n')
self.doneq.task_done()
if self.q.empty():
self.scrolltxt.insert(tk.END, 'no more jobs\n')
self.scrolltxt.see(tk.END)
self.win.after(200,self.syncGUI)
q ist die job-queue welche der thread abarbeitet,doneq ist die queue für die Rückmeldung.
Warum darf der thread die gui nicht direkt updaten? Hat auch funktioniert...könnte evtl. bei mehreren threads Probleme bereiten bei gleichzeitigem Zugriff.
Re: queue+threads
Verfasst: Montag 16. August 2021, 09:52
von __deets__
Das hat funktioniert wie es auch funktioniert, mit geschlossenen Augen über die Straße zu gehen. Ohne vorher zu schauen, ob ein Auto kommt. Man stirbt nicht jedes Mal. Aber man *kann* sterben.
Die Innereien der GUI sind nicht davor geschützt, von mehreren Threads gleichzeitig bearbeitet zu werden. Dadurch können dann inkonsistente Zustände entstehen, die zu Abstürzen führen *können*. Nicht müssen. So wie man eben nicht zwangsläufig umkommt, wenn man ohne zu schauen auf die Straße rennt.
Re: queue+threads
Verfasst: Montag 16. August 2021, 10:03
von __blackjack__
@frank-w: Ich würde das ohne `empty()` machen, mit einer ``while True:``-Schleife die dann verlassen wird wenn das `get_nowait()` eine Ausnahme auslöst weil die Queue tatsächlich leer ist.
Methodennamen übrigens klein_mit_unterstrichen.
