Variable einer Klasse in anderem Thread überprüfen?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo zusammen,

folgendes Szenario in meinem Daten-Logger:

> HauptProgramm initialisiert die GUI und ein paar Variablen wie auch self.status = True / False
> Nach Betätigung eines Buttons "Aufnahme" werden z.B. 3 Threads "AufnahmeThreads" gestartet.
> Nach ihrer Initialisierung wird die run-Methode gestartet in der sich eine Endlosschleife gefolgt von einer Abfrage befindet. Diese Abfrage soll überprüfen ob der Thread noch Aufzeichnen (if-zweig) oder beendet werden soll (else-zweig)

Meine Fragen dazu sind:
1. Wie kann ich den permanenten Status in meinen Threads abfragen, denn self.status kennen diese ja nicht und übergeben brauche ich sie auch nicht, da sie dann ja nur einmal bei der Übergabe den Wert erhalten und nicht permanent aktualisiert werden. Mit if Hauptprogramm.status: habe ich es auch probiert, jedoch hat das nicht geklappt. Müsste ich hier auch mit Queues arbeiten oder gibt es einen einfacheren Weg?
2. Gibt es eine bestimmte Methode um einen Thread sauber zu beenden? oder reicht es wenn in der run-Methode keine weitere Anweisung enthalten ist, also im else-zweig nur noch z.B. print("Aufnahme Thread # beendet") steht?

Vielen dank und lg Tina
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst die INSTANZ deines Hauptprogramms uebergeben, um darauf Werte abzupruefen. Also

Code: Alles auswählen

class Hauptprogramm:

      def __init__(self):
                self._workers = [Worker(self) for _ in range(3)]


Dann kannst du in einem Worker (der sich das uebergebene Objejt natuerlich merken muss) testen, ob zB self._main_program.running immer noch wahr ist.

Streng genommen muesste man diesen Zugriff dann auch noch ueber ein Lock absichern, da Python aber ein Global Interpreter Lock hat, lasse ich das bei so etwas simplen sein.

Und der Thread muss einfach nur "auslaufen", ein anderes abraeumen gibt es nicht.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Du mußt allen Deinen Threads ein Event übergeben, das solange es nicht gesetzt ist, die Schleife wiederholt:

Code: Alles auswählen

def work(terminate):
    while not terminate.is_set():
        do_something()

Code: Alles auswählen

class Main():
    def start(self):
        self.terminate = threading.Event()
        threading.Thread(target=work, args=(self.terminate,)).start()

    def stop(self):
        self.terminate.set()
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Ah ok, leider hab ich dein Beispiel nicht ganz kapiert :) sorry.

Aktuell gehe ich so vor (Ausschnitte):

Code: Alles auswählen

class AufnahmeThread(threading.Thread)
	def __init__(self, hauptprog, ...):
		threading.Thread.__init__(self)
		self.hp= hauptprog
		....
		
	def run(self):
		...
		if not self.hp.status:
			print("Aufnahme läuft weiter")
		else:
			print("Aufnahme wird beendet!")
		
		
class HauptProgramm(QMainWindow ...)
	def __init__(self):
		super(MainWindow, self).__init__()
		self.setupUi(self)
		
		self.status = False
		self.numThreads = 3
		
		self.buttondrucken.connect(self.drueckeButton)
		
		
	def drueckeButton(self):
		self.status = True
		
	def erzeugeThreads(self):
		for zaehler in range (self.numThreads):
			self.AufnahmeThread = AufnahmeThread(self, hauptprog, ...)
			self.AufnahmeThread.start()
			
def main():
	app = QApplication(sys.argv)
	app.setApplicationName("Test")
	gui = MainWindow()
	gui.show()
	sys.exit(app.exec_())
	
if __Name__  == '__main__':
	main()

So haut es nun leider nicht hin :/
Wo meinst du muss ich die Instanz übergeben? Beim erstellen der Threads?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja, das sind doch worker-threads. Die muessen eben die HaupProgramm-Instanz bekommen. Die heisst ja "self" and der Stelle, an der du die Threads erzeugst. Und im Thread musst du natuerlich eine while-Schleife haben, sonst macht der nur einmal kurz etwas.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Allgemein zum Code: Abkürzungen vermeiden, hp ist völlig kryptisch und hauptprog kannst Du auch noch ram spendieren. Variablen schreibt man klein_mit_unterstricht (num_threads, -> number_of_threads). drueckeButton macht genau das nicht. Es ist ein Handler, wenn ein Button gedrückt wurde, der generische Name sagt aber auch nichts, was dann passiert -> stop_threads. __Name__ ist falsch geschrieben.
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mit Tabs.
Dass jeder Thread an das selbe Attribut gebunden wird, ist quatsch, weil so ja nur der letzte übrig bleibt. Also entweder brauchst Du eine Liste oder eine lokale Variable reicht. Wenn ein Thread nur aus einer run-Methode besteht, schreibt man keine eigene Klasse, sondern eine einfache Funktion, die man threading.Thread als target-Argument übergibt (siehe mein Beispiel).

Und wie schon geschrieben, sollte HauptProgramm.status vom Typ Event sein.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nur aus Interesse: Welche Nachteile hat es, wie vorgeschlagen, die Instanz der betroffenen Klasse anstelle des Events zu übergeben? Ich hätte wohl auch das Event genommen, weil ich es so in der Threadprogrammierung kenne, aber wäre das andere Vorgehen fehleranfällig?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Man kann ja auch die Instanz der betroffenen Klasse übergeben, solange die ein `Event` als Attribut hat. ;-)

Man kann sich natürlich auf den Standpunkt stellen das einfacher Attrbibutzugriff schon atomar sein wird, wegen GIL bei CPython. Man kann aber auch argumentieren das CPython und GIL nicht alles sind was es gibt, und selbst dort darf man das Attribut nicht durch ein Property ersetzen oder den Wert über `__getattr__()` oder `__getattribute__()` zur Verfügung stellen, denn das ist dann nicht mehr garantiert atomar. `Event` ist einfach die sichere (Standard)Lösung die man aus der Threadprogrammierung kennt.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo zusammen, danke für den Hinweise. Jetzt hab ich es auch kapiert. Musste mich dort etwas einlesen, denn das kannte ich vorher nocht nicht :-)
Jetzt läuft es, aber die nächste Frage kommt sicher.
Vielen Dank und ein schönes Wochenende an alle
Tina
Antworten