Wie kann ich eine globale Variable mit einem Thread teilen?
Mein Python Code Beispiel ist:
from threading import Thread
import time
a = 0 #global variable
def thread1(threadname):
#read variable "a" modify by thread 2
def thread2(threadname):
while 1:
a += 1
time.sleep(1)
thread1 = Thread( target=thread1, args=("Thread-1", ) )
thread2 = Thread( target=thread2, args=("Thread-2", ) )
thread1.join()
thread2.join()
Ich weiß nicht, wie man die beiden Threads dazu bringt, sich eine Variable zu teilen.
Sie müssen nur a
als global in thread2
deklarieren, so dass Sie nicht ein a
ändern, das lokal in dieser Funktion ist.
def thread2(threadname):
global a
while True:
a += 1
time.sleep(1)
In thread1
brauchen Sie nichts Besonderes zu tun, solange Sie nicht versuchen, den Wert von a
zu ändern (was eine lokale Variable erzeugen würde, die die globale überschattet; verwenden Sie global a
, wenn Sie das müssen)>
def thread1(threadname):
#global a # Optional if you treat a as read-only
while a < 10:
print a
In einer Funktion:
a += 1
wird vom Compiler als Assign to a => Create local variable a
interpretiert, was nicht das ist, was Sie wollen. Es wird wahrscheinlich mit einem a not initialized
Fehler fehlschlagen, da das (lokale) a tatsächlich nicht initialisiert wurde:
>>> a = 1
>>> def f():
... a += 1
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment
Sie koennten das Gewuenschte mit dem (sehr verpönten und aus guten Gruenden) Schluesselwort global
erreichen, etwa so:
>>> def f():
... global a
... a += 1
...
>>> a
1
>>> f()
>>> a
2
Im Allgemeinen sollten Sie es jedoch vermeiden, globale Variablen zu verwenden, die sehr schnell außer Kontrolle geraten. Und das gilt besonders für Multithreading-Programme, bei denen Sie keinen Synchronisationsmechanismus haben, damit Ihr thread1
weiß, wann a
geändert worden ist. Kurz gesagt: Threads sind kompliziert, und man kann nicht erwarten, dass man ein intuitives Verständnis für die Reihenfolge der Ereignisse hat, wenn zwei (oder mehr) Threads am selben Wert arbeiten. Die Sprache, der Compiler, das Betriebssystem, der Prozessor... können ALLE eine Rolle spielen und entscheiden, die Reihenfolge der Operationen aus Gründen der Geschwindigkeit, der Praktikabilität oder aus anderen Gründen zu ändern.
Der richtige Weg für solche Dinge ist die Verwendung von Python Sharing Tools (locks und Freunde), oder besser noch, Daten über eine Queue zu kommunizieren, anstatt sie zu teilen, z.B. so:
from threading import Thread
from queue import Queue
import time
def thread1(threadname, q):
#read variable "a" modify by thread 2
while True:
a = q.get()
if a is None: return # Poison pill
print a
def thread2(threadname, q):
a = 0
for _ in xrange(10):
a += 1
q.put(a)
time.sleep(1)
q.put(None) # Poison pill
queue = Queue()
thread1 = Thread( target=thread1, args=("Thread-1", queue) )
thread2 = Thread( target=thread2, args=("Thread-2", queue) )
thread1.start()
thread2.start()
thread1.join()
thread2.join()
Nun, ein laufendes Beispiel:
WARNUNG! NIEMALS ZU HAUSE/ArBEITEN! Nur im Klassenzimmer ;)
Verwenden Sie Semaphoren, gemeinsam genutzte Variablen usw., um Eilzustände zu vermeiden.
from threading import Thread
import time
a = 0 # global variable
def thread1(threadname):
global a
for k in range(100):
print("{} {}".format(threadname, a))
time.sleep(0.1)
if k == 5:
a += 100
def thread2(threadname):
global a
for k in range(10):
a += 1
time.sleep(0.2)
thread1 = Thread(target=thread1, args=("Thread-1",))
thread2 = Thread(target=thread2, args=("Thread-2",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
und die Ausgabe:
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 2
Thread-1 3
Thread-1 3
Thread-1 104
Thread-1 104
Thread-1 105
Thread-1 105
Thread-1 106
Thread-1 106
Thread-1 107
Thread-1 107
Thread-1 108
Thread-1 108
Thread-1 109
Thread-1 109
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Wenn das Timing richtig wäre, würde die Operation a += 100
übersprungen werden:
Der Prozessor führt bei T a+100
aus und erhält 104. Aber er hält an und springt zum nächsten Thread
Hier wird bei T+1 a+1`` mit dem alten Wert von a ausgeführt,
a == 4. Es wird also 5 berechnet. Springe zurück (bei T+2), Thread 1, und schreibe
a=104in den Speicher. Jetzt zurück zu Thread 2, Zeit ist T+3 und schreibe
a=5``` in den Speicher.
Voila! Die nächste Druckanweisung wird 5 statt 104 ausgeben.
Ein SEHR unangenehmer Fehler, der reproduziert und gefangen werden muss.