Usando una variable global con un hilo

¿Cómo comparto una variable global con hilo?

El ejemplo de mi código de 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 hacer que los dos hilos compartan una variable.

Solo necesita declarar a como global en el thread2 , para no estar modificando un a que sea local para esa función.

 def thread2(threadname): global a while True: a += 1 time.sleep(1) 

En el thread1 , no necesita hacer nada especial, siempre que no intente modificar el valor de a (lo que crearía una variable local que sombrea la global; use global a si es necesario)>

 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 comstackdor como assign to a => Create local variable a , que no es lo que desea. Probablemente fallará con un error a not initialized ya que, de hecho, el (local) a no se ha inicializado:

 >>> a = 1 >>> def f(): ... a += 1 ... >>> f() Traceback (most recent call last): File "", line 1, in  File "", line 2, in f UnboundLocalError: local variable 'a' referenced before assignment 

Puede obtener lo que quiere con la palabra clave global (muy mal visto y por buenas razones), como:

 >>> def f(): ... global a ... a += 1 ... >>> a 1 >>> f() >>> a 2 

En general, sin embargo, debe evitar el uso de variables globales que se vuelven extremadamente rápidamente fuera de control. Y esto es especialmente cierto para los progtwigs multiproceso, donde no tiene ningún mecanismo de sincronización para que su thread1 sepa cuándo se ha modificado uno. En resumen: los subprocesos son complicados y no puede esperar tener una comprensión intuitiva del orden en que suceden los eventos cuando dos (o más) subprocesos trabajan en el mismo valor. El lenguaje, el comstackdor, el sistema operativo, el procesador … TODOS pueden desempeñar un papel, y decidir modificar el orden de las operaciones por velocidad, practicidad o cualquier otra razón.

La forma adecuada para este tipo de cosas es usar las herramientas de intercambio de Python ( cerraduras y amigos), o mejor dicho, comunicar los datos a través de una cola en lugar de compartirlos, por ejemplo, de esta manera:

 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() 

Se debe considerar el uso de un locking, como el threading.Lock . Bloqueo. Ver objetos de locking para más información.

La respuesta aceptada PUEDE imprimir 10 por hilo1, que no es lo que quieres. Puede ejecutar el siguiente código para entender el error más fácilmente.

 def thread1(threadname): while True: if a % 2 and not a % 2: print "unreachable." def thread2(threadname): global a while True: a += 1 

El uso de un candado puede prohibir el cambio de a tiempo leyendo más de una vez:

 def thread1(threadname): while True: lock_a.acquire() if a % 2 and not a % 2: print "unreachable." lock_a.release() def thread2(threadname): global a while True: lock_a.acquire() a += 1 lock_a.release() 

Si el hilo usa la variable durante mucho tiempo, copiarla a una variable local primero es una buena opción.

Bueno, corriendo ejemplo:

¡ADVERTENCIA! ¡NUNCA HAGAS ESTO EN CASA / TRABAJAR! Solo en el aula;)

Use 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 el tiempo fuera correcto, la operación a a += 100 se omitirá:

El procesador se ejecuta en T a+100 y obtiene 104. Pero se detiene y pasa al siguiente hilo Aquí, en T + 1 ejecuta a+1 con el valor antiguo de a, a == 4 . Entonces calcula 5. Salta hacia atrás (en T + 2), subproceso 1 y escribe a=104 en la memoria. Ahora, volviendo al hilo 2, el tiempo es T + 3 y escribe a=5 en la memoria. Voila! La siguiente instrucción de impresión imprimirá 5 en lugar de 104.

MUY desagradable error para ser reproducido y capturado.

Muchas gracias Jason Pan por sugerir ese método. La instrucción thread1 if no es atómica, de modo que mientras se ejecuta esa instrucción, es posible que thread2 se introduzca en thread1, lo que permite alcanzar un código no accesible. He organizado ideas de las publicaciones anteriores en un progtwig de demostración completo (a continuación) que ejecuté con Python 2.7.

Con un análisis cuidadoso, estoy seguro de que podríamos obtener más información, pero por ahora creo que es importante demostrar lo que sucede cuando el comportamiento no atómico se encuentra con el hilo.

 # ThreadTest01.py - Demonstrates that if non-atomic actions on # global variables are protected, task can intrude on each other. from threading import Thread import time # global variable a = 0; NN = 100 def thread1(threadname): while True: if a % 2 and not a % 2: print("unreachable.") # end of thread1 def thread2(threadname): global a for _ in range(NN): a += 1 time.sleep(0.1) # end of thread2 thread1 = Thread(target=thread1, args=("Thread1",)) thread2 = Thread(target=thread2, args=("Thread2",)) thread1.start() thread2.start() thread2.join() # end of ThreadTest01.py 

Como se predijo, al ejecutar el ejemplo, a veces se alcanza el código “inalcanzable”, que produce resultados.

Solo para agregar, cuando inserté un par de adquisición / liberación de locking en el hilo1, descubrí que la probabilidad de que se imprimiera el mensaje “inalcanzable” se redujo considerablemente. Para ver el mensaje reduje el tiempo de inactividad a 0.01 segundos y aumenté NN a 1000.

Con un par de locking / adquisición en el hilo 1 no esperaba ver el mensaje, pero está ahí. Después de que inserté un par de adquisición / liberación de locking también en el hilo 2, el mensaje ya no apareció. En el signo posterior, la statement de incremento en el subproceso 2 probablemente también sea no atómica.