Hi,
gehen wir davon aus ich hab ein Programm das einen weiteren Thread eröffnent. Der Thread und das Programm laufen beständig weiter müssen aber während der laufzeit daten austauschen. Gibt es hier eine Möglichkeit auf Globale variablen zu verzichten?
Mit return kann man ja nicht arbeiten da sonst der Thread aufhören würde.
Schöne Grüße
Miko
Threadprogrammierung mit Variablen
Die sicherste und einfachste Kommunikation kann man über `Queue.Queue()`-Objekte abwickeln.
danke erstmal für die antworten.
ich will aber nicht dass objekte übergeben werden indem die eine funktion übergibt und die andere empfängt sondern dass nur die funktion die die variable ändert dies irgendwie hinterlegt. geht das mit queue ?
ich will aber nicht dass objekte übergeben werden indem die eine funktion übergibt und die andere empfängt sondern dass nur die funktion die die variable ändert dies irgendwie hinterlegt. geht das mit queue ?
Nicht so direkt, nein. Du kannst natürlich mit einer Funktion beliebige Attribute von Objekten ändern die in beiden Threads bekannt sind, aber dann musst Du auf die ganzen Sachen achten die bei nebenläufiger Programmierung so passieren können, "race conditions", "deadlocks" usw. und da selber Sperren setzen um das zu verhindern. Queues verwenden intern schon die notwendigen Sperrmechanismen, da hat man wenig Sorgen mit.
Was genau willst Du denn machen?
Was genau willst Du denn machen?
hm eigentlich nur die gleichzeitige eingabe und den ablauf des programms. das funktioniert mit globalen variablen schon ganz gut.
Das funktioniert nicht, ohne diese speziell (bwpw. durch threading.Lock) zu schützen. Da ist Queue.Queue einfacher, sonst hast du die gleichen Probleme, wie bereits in den vorangegangenen Posts erwähnt.Miko hat geschrieben:Ich hab die lösung ... ich nehm einfach klassenvariablen XD
- gerold
- Python-Forum Veteran
- Beiträge: 5555
- Registriert: Samstag 28. Februar 2004, 22:04
- Wohnort: Oberhofen im Inntal (Tirol)
- Kontaktdaten:
Hallo!EyDu hat geschrieben:Das funktioniert nicht, ohne diese speziell (bwpw. durch threading.Lock) zu schützen.
Ich habe hier mal ein Beispiel für die Verwendung eines Locks, bin mir aber nicht sicher, ob dieser Aufwand wirklich notwendig ist. Dafür kenne ich mich zu wenig mit den Python-Interna aus.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
import threading
class ThreadSafeAttributes(object):
def __init__(self):
object.__init__(self)
object.__setattr__(self, "_lock", threading.Lock())
def __getattribute__(self, name):
lock = object.__getattribute__(self, "_lock")
lock.acquire()
attrvalue = object.__getattribute__(self, name)
lock.release()
return attrvalue
def __setattr__(self, name, value):
lock = object.__getattribute__(self, "_lock")
lock.acquire()
object.__setattr__(self, name, value)
lock.release()
def main():
"""testen"""
import time
def testthreadfunc(tsa):
tsa.vorname = "Gerold"
print tsa.vorname
time.sleep(0.9)
tsa.vorname = "Martin"
print tsa.vorname
tsa = ThreadSafeAttributes()
t1 = threading.Thread(target = testthreadfunc, args = (tsa, ))
t2 = threading.Thread(target = testthreadfunc, args = (tsa, ))
t3 = threading.Thread(target = testthreadfunc, args = (tsa, ))
t1.start()
time.sleep(0.1)
t2.start()
time.sleep(0.1)
t3.start()
t1.join()
t2.join()
t3.join()
if __name__ == "__main__":
main()
lg
Gerold
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
In den meisten Fällen wird es sicher ohne einen Lock gehen, aber selbst wenn nur bei jedem 1000-sten Durchlauf etwas nicht funktioniert, ist das Programm einfach nicht korrekt.gerold hat geschrieben:Ich habe hier mal ein Beispiel für die Verwendung eines Locks, bin mir aber nicht sicher, ob dieser Aufwand wirklich notwendig ist. Dafür kenne ich mich zu wenig mit den Python-Interna aus.
Das Beispiel ist schon ganz OK so, mann sollte die kritischen Abschnitte nur noch mit einem try-finally umwickeln, da man sich im Fehlerfall, oder gar einer gewollten Exception, sonst sehr leicht einen Deadlock einfängt:
Code: Alles auswählen
def __getattribute__(self, name):
lock = object.__getattribute__(self, "_lock")
lock.acquire()
try:
return object.__getattribute__(self, name)
finally:
lock.release()
def __setattr__(self, name, value):
lock = object.__getattribute__(self, "_lock")
lock.acquire()
try:
object.__setattr__(self, name, value)
finally:
lock.release()
Seit 2.5 gibt es noch die wunderschöne Variante über das with-Statement:
Code: Alles auswählen
def __getattribute__(self, name):
with object.__getattribute__(self, "_lock"):
return object.__getattribute__(self, name)
def __setattr__(self, name, value):
with object.__getattribute__(self, "_lock"):
object.__setattr__(self, name, value)
Ich hoff ich muss mich jetzt nicht schämen wenn ich das nicht gleich versteh.
Fehler gibt es doch nur wenn 2 threads gleichzeitig probieren eine variable zu ändern oder?
Das dürfte doch theoretisch garnicht möglich sein da der prozessor doch nur eine aufgabe gleichzeitig erledigen kann (dualcores mal ausgeschlossen).
kann es somit doch zu fehlern bei einprozessoresystemen kommen?
Fehler gibt es doch nur wenn 2 threads gleichzeitig probieren eine variable zu ändern oder?
Das dürfte doch theoretisch garnicht möglich sein da der prozessor doch nur eine aufgabe gleichzeitig erledigen kann (dualcores mal ausgeschlossen).
kann es somit doch zu fehlern bei einprozessoresystemen kommen?
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Die Fehler gibt es, wenn du auf eine Variable zugreifst, stell dir Folgende Situation vor: Du hast zwei Threads, einer führt den nachfolgenden Code aus und einer hängt einen String als letztes Zeichen zu einem bestimmten Zeitpunkt 'b' an die Variable ``var`` an.Miko hat geschrieben:Fehler gibt es doch nur wenn 2 threads gleichzeitig probieren eine variable zu ändern oder?
Das dürfte doch theoretisch garnicht möglich sein da der prozessor doch nur eine aufgabe gleichzeitig erledigen kann (dualcores mal ausgeschlossen).
kann es somit doch zu fehlern bei einprozessoresystemen kommen?
Code: Alles auswählen
if var.endswith('a'):
var = var[0:-2] + 2* var[-1]
P.S.: Tut mir leid für das blöde Beispiel, ich hoffe es kommt das rüber, was ich ausdrücken will, aber um viertel nach eins ist mir nichts besseres eingefallen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
- Rebecca
- User
- Beiträge: 1662
- Registriert: Freitag 3. Februar 2006, 12:28
- Wohnort: DN, Heimat: HB
- Kontaktdaten:
Es reicht auch schon sowas wie
Was passiert? Der Wert von a wird aus dem Speicher in ein Register gelesen, es wird eins hinzuaddiert, dann wird das Ergebnis wieder im Speicher abgelegt. Obwohl dieser ganze Vorgang in Python nur ein einziges Statement ist, ist das intern keine atomare Operation. Es sind mehrere Arbeitsschritte notwendig, zwischen denen ein Threadwechsel stattfinden kann. Fuehren nun zwei Threads diese Zeile aus, und passiert ein Threadwechsel jeweils genau nach dem Einlesen des Werts von a, dann zaehlen beide Threads "ihr" a hoch, speichern es wieder... wobei ein Hochzaehlen verloren geht.
Egal, ob echte Gleichzeitigkeit auf mehreren CPUs oder scheinbare Gleichzeitigkeit auf einer CPU, die Probleme bleiben die gleichen. Das Dumme ist, dass so ein Fehler nur hoechst selten auch wirklich mal auftritt, und dann steht man dumm da...
Code: Alles auswählen
a += 1
Egal, ob echte Gleichzeitigkeit auf mehreren CPUs oder scheinbare Gleichzeitigkeit auf einer CPU, die Probleme bleiben die gleichen. Das Dumme ist, dass so ein Fehler nur hoechst selten auch wirklich mal auftritt, und dann steht man dumm da...
- gerold
- Python-Forum Veteran
- Beiträge: 5555
- Registriert: Samstag 28. Februar 2004, 22:04
- Wohnort: Oberhofen im Inntal (Tirol)
- Kontaktdaten:
Hallo!
Die Frage ist ja immer noch, ob es überhaupt notwendig ist, allein den Zugriff auf eine gemeinsam genutzte Variable mit einem Lock zu versehen, oder ob es damit nie ein Problem geben kann.
Dass man Algorithmen, die auf eine "gemeinsam genutzte Variable" zugreifen, mit einem Lock absichern sollte ist jetzt sicher klar, aber kann auch einfach nur der Zugriff (lesend oder schreibend) Schwierigkeiten machen? Wahrscheinlich nicht, oder doch?
Wenn nicht, dann wäre die oben von mir entworfene Klasse für den Zugriff auf einfache Variablen (int, str) total umsonst.
Je mehr ich darüber nachdenke, desto mehr glaube ich, dass sich Python intern (Stichwort: GIL) darum kümmert, dass sich Threads beim Zugriff auf eine Variable nicht in die Quere kommen.
Wichtig wäre hier eine Aussage von jemandem, der sich mit dieser Thematik besser auskennt -- also näher an der Entwicklung von Python dran ist.
Edit:
mfg
Gerold
Die Frage ist ja immer noch, ob es überhaupt notwendig ist, allein den Zugriff auf eine gemeinsam genutzte Variable mit einem Lock zu versehen, oder ob es damit nie ein Problem geben kann.
Dass man Algorithmen, die auf eine "gemeinsam genutzte Variable" zugreifen, mit einem Lock absichern sollte ist jetzt sicher klar, aber kann auch einfach nur der Zugriff (lesend oder schreibend) Schwierigkeiten machen? Wahrscheinlich nicht, oder doch?
Wenn nicht, dann wäre die oben von mir entworfene Klasse für den Zugriff auf einfache Variablen (int, str) total umsonst.
Je mehr ich darüber nachdenke, desto mehr glaube ich, dass sich Python intern (Stichwort: GIL) darum kümmert, dass sich Threads beim Zugriff auf eine Variable nicht in die Quere kommen.
Wichtig wäre hier eine Aussage von jemandem, der sich mit dieser Thematik besser auskennt -- also näher an der Entwicklung von Python dran ist.
Edit:
Damit wäre das Sperren, bei jedem Zugriff auf eine gemeinsam genutzte Variable sinnlos und man müsste sich nur um logische Fehler im Code kümmern...BlackJack hat geschrieben:Du kannst natürlich mit einer Funktion beliebige Attribute von Objekten ändern die in beiden Threads bekannt sind,
mfg
Gerold
Zuletzt geändert von gerold am Donnerstag 31. Mai 2007, 08:46, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Miko hat geschrieben:hm eigentlich nur die gleichzeitige eingabe und den ablauf des programms. das funktioniert mit globalen variablen schon ganz gut.
Pfui, globale variablen als kommunikationsweg....
Warum nicht Queue objekte?
Benutze eben einen thread für die "arbeit" und einen für eingaben.
Dazwischen eine Queue, z.b. "eingabe_queue".
Der worker checkt regelmässig ob in der eingabe queue etwas liegt und reagiert darauf.
Besser als jegliches selbst gefrickeltes.
- gerold
- Python-Forum Veteran
- Beiträge: 5555
- Registriert: Samstag 28. Februar 2004, 22:04
- Wohnort: Oberhofen im Inntal (Tirol)
- Kontaktdaten:
Hallo Mad-Marty!Mad-Marty hat geschrieben:Warum nicht Queue objekte?
Benutze eben einen thread für die "arbeit" und einen für eingaben.
Dazwischen eine Queue, z.b. "eingabe_queue".
Der worker checkt regelmässig ob in der eingabe queue etwas liegt und reagiert darauf.
Queue-Objekte sind nicht das Allheilmittel. Sie sind super, wenn ich z.B. einen Thread blockieren lassen möchte, bis ein neuer Wert übergeben wird, oder wenn immer wieder verschiedene Werte von einem Hauptthread übergeben werden müssen.
ABER sie bringen nichts, wenn mehrere Threads "gleichzeitig" mit den selben Werten arbeiten müssen. Z.B. ein Counter. oder bei einem Poker-Spiel die Einsätze der Mitspieler. Man könnte den neuen Einsatz zwar in eine Queue legen, aber dann kann nur ein Thread etwas damit anfangen. --> Genau der Thread, der sich den Wert als Erster aus der Queue holt. Man müsste also immer einen Thread haben, der den Wert wieder verteilt.
Ja, es ist natürlich mit Queues machbar, aber die Frage ist, ob es überhaupt notwendig ist. Genügt es vielleicht schon, von einem Thread aus eine Variable zu setzen, auf die die anderen Threads ebenfalls Zugriff haben? Das macht das Programm nicht so kompliziert wie es z.B. mit Queues sein kann.
Alles was ich hier geschrieben habe gilt nur dann, wenn es kein Problem beim "einfachen" Zugriff auf eine "einfache" Variable (z.B. str oder int) geben kann. Das ist für mich aber im Moment noch nicht bestätigt.
Wenn es aber bestätigt wird, dann spricht nichts gegen den gemeinsamen Zugriff auf eine Variable. Dann muss nur noch die Verwendung des "global"-Statements eingeschränkt werden. Z.B. durch Auslagern der gemeinsam benutzten Werte in eine gemeinsam genutzte Klasseninstanz.
mfg
Gerold
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
- gerold
- Python-Forum Veteran
- Beiträge: 5555
- Registriert: Samstag 28. Februar 2004, 22:04
- Wohnort: Oberhofen im Inntal (Tirol)
- Kontaktdaten:
Hallo!
Jetzt weiß ich auch wieder, warum ich mir nicht sicher bin, ob der einfache Variablezugriff Schwierigkeiten bereiten kann.
Ich glaube, in folgendem Beispiel gab es Ungereimtheiten (bei der Verwendung mit wxPython), die sich erst legten, als ich die Statusvariable in ein Event umwandelte. (ganz sicher bin ich mir allerdings nicht )
In diesem Beispiel wird die Methode "stop" von einem anderen Thread aufgerufen.
mfg
Gerold
Jetzt weiß ich auch wieder, warum ich mir nicht sicher bin, ob der einfache Variablezugriff Schwierigkeiten bereiten kann.
Ich glaube, in folgendem Beispiel gab es Ungereimtheiten (bei der Verwendung mit wxPython), die sich erst legten, als ich die Statusvariable in ein Event umwandelte. (ganz sicher bin ich mir allerdings nicht )
In diesem Beispiel wird die Methode "stop" von einem anderen Thread aufgerufen.
Code: Alles auswählen
import threading
class HatSchwierigkeitenBereitet(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.canceled = False
def run(self):
while True:
if self.canceled:
break
print "."
def stop(self):
self.canceled = True
class Funktioniert(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.canceled = threading.Event()
def run(self):
while True:
if self.canceled.isSet():
break
print "."
def stop(self):
self.canceled.set()
class FunktioniertWahrscheinlichAuch_IstAberEvtNichtNotwendig(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.canceled = False
self.lock = threading.Lock()
def run(self):
while True:
self.lock.acquire()
try:
if self.canceled:
break
finally:
self.lock.release()
print "."
def stop(self):
self.lock.acquire()
try:
self.canceled = True
finally:
self.lock.release()
Gerold
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
@gerold
Deine letzten beiden Beispiele sind im Prinzip identisch, du hast lediglich "threading.Event" nachgebaut und quasi zur Weitergabe von beliebitgen Objekten erweitert.
Dein erstes Beispiel wird früher oder später Probleme bereiten. Momentan ist es zwar so, dass eine Anweisung, die direkt in Bytecode umgewandelt werden kann nicht unterbrechbar ist (dafür ist der GIL da), aber das wird sicherlich nicht ewig so bleiben. Wenn ich mich richtig erinnere, meine ich schon von einer inoffiziellen Version gehört zu haben, die diese Beschränkung nicht mehr hat (vielleicht kennt ja jemand den Namen).
In deinem Beispiel hingegen hat man neben der Zuweisung auf self.canceled (was glaube ich in eine einzige Bytecode-Anweisung umgesetzt wird) noch die Auflösung von "self". Das scheint zwar noch nicht kritisch zu sein, aber wenn man jedes mal auf so etwas achten muss, geht bei irgend einer Änderung mit Sicherheit etwas schief.
Deine letzten beiden Beispiele sind im Prinzip identisch, du hast lediglich "threading.Event" nachgebaut und quasi zur Weitergabe von beliebitgen Objekten erweitert.
Dein erstes Beispiel wird früher oder später Probleme bereiten. Momentan ist es zwar so, dass eine Anweisung, die direkt in Bytecode umgewandelt werden kann nicht unterbrechbar ist (dafür ist der GIL da), aber das wird sicherlich nicht ewig so bleiben. Wenn ich mich richtig erinnere, meine ich schon von einer inoffiziellen Version gehört zu haben, die diese Beschränkung nicht mehr hat (vielleicht kennt ja jemand den Namen).
In deinem Beispiel hingegen hat man neben der Zuweisung auf self.canceled (was glaube ich in eine einzige Bytecode-Anweisung umgesetzt wird) noch die Auflösung von "self". Das scheint zwar noch nicht kritisch zu sein, aber wenn man jedes mal auf so etwas achten muss, geht bei irgend einer Änderung mit Sicherheit etwas schief.
- Rebecca
- User
- Beiträge: 1662
- Registriert: Freitag 3. Februar 2006, 12:28
- Wohnort: DN, Heimat: HB
- Kontaktdaten:
Ich haette jetzt vermutet, dass sich die ersten beiden theoretisch gleich verhalten muessten (mal davon abgesehen, dass etwas anderer Code ausgefuehrt wird, sodass sie sich praktisch doch wieder anders verhalten). Es koennte ein Kontextwechsel zwischen der if-Abfrage und dem break auftreten, sodass die anschliessende print-Ausgabe noch ausgefuehrt wird, wenn self.cancel schon auf True steht. Lediglich das letzte Beispiel verhindert dies, da ein Lock um beide Statements gelegt wird.
Ist natuerlich die Frage, was hier "funktioniert (nicht)" bedeutet.
Ist natuerlich die Frage, was hier "funktioniert (nicht)" bedeutet.
wobei meistens braucht man ja nicht eine variable in 2 threads zum schreiben.
und wenn ein thread schreibt und einer liest dürfts doch keine probleme geben oder?
Dinge wie lock und queue kenn ich noch nicht deswegen blick ich da noch nicht ganz durch.
Ich benutze auch immer das Modul Thread und nicht threading!
Aber danke für die zahlreichen antworten
und wenn ein thread schreibt und einer liest dürfts doch keine probleme geben oder?
Dinge wie lock und queue kenn ich noch nicht deswegen blick ich da noch nicht ganz durch.
Ich benutze auch immer das Modul Thread und nicht threading!
Aber danke für die zahlreichen antworten