¿Cómo comparto una variable global con un hilo?
Mi ejemplo de código Python es:
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()
No sé cómo conseguir que los dos hilos compartan una variable.
Sólo tienes que declarar a
como global en thread2
, para que no estés modificando un a
que es local a esa función.
def thread2(threadname):
global a
while True:
a += 1
time.sleep(1)
En thread1
, no necesitas hacer nada especial, siempre y cuando no intentes modificar el valor de a
(lo que crearía una variable local que haría sombra a la global; usa global a
si lo necesitas)>
def thread1(threadname):
#global a # Optional if you treat a as read-only
while a < 10:
print a
En una función:
a += 1
será interpretado por el compilador como assign to a => Create local variable a
, que no es lo que quieres. Probablemente fallará con un error a not initialized
ya que la variable (local) a no ha sido inicializada:
>>> 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
Podrías conseguir lo que quieres con la (muy mal vista, y por buenas razones) palabra clave global
, así:
>>> def f():
... global a
... a += 1
...
>>> a
1
>>> f()
>>> a
2
En general, sin embargo, usted debe evitar el uso de variables globales que se convierten muy rápidamente fuera de control. Y esto es especialmente cierto para programas multihilo, donde no tienes ningún mecanismo de sincronización para que tu hilo1
sepa cuando a
ha sido modificado. En resumen: los hilos son complicados, y no puedes esperar tener una comprensión intuitiva del orden en el que se suceden los eventos cuando dos (o más) hilos trabajan sobre el mismo valor. El lenguaje, el compilador, el SO, el procesador... TODOS pueden jugar un papel, y decidir modificar el orden de las operaciones por velocidad, practicidad o cualquier otra razón.
Lo adecuado para este tipo de cosas es utilizar herramientas de compartición de Python (cerraduras y amigos), o mejor, comunicar los datos a través de una Cola en lugar de compartirlos, por ejemplo así:
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()
Bueno, corriendo ejemplo:
¡ADVERTENCIA! NUNCA HAGAS ESTO EN CASA / TRABAJO Sólo en el aula ;)
Utiliza semáforos, variables compartidas, etc. para evitar condiciones de apuro.
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()
y la salida:
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
Si la sincronización fuera correcta, se omitiría la operación `a += 100
:
El procesador ejecuta en T a+100`` y obtiene 104. Pero se detiene, y salta al siguiente hilo Aquí, en T+1 ejecuta
a+1con el valor antiguo de a, ```a == 4```. Así que calcula 5. Salta atrás (a T+2), hilo 1, y escribe ```a=104
en memoria.
Ahora vuelve al hilo 2, el tiempo es T+3 y escribe `a=5
en memoria.
¡Voilà! La siguiente instrucción print imprimirá 5 en lugar de 104.
Un bug MUY desagradable que hay que reproducir y atrapar.