¿Qué es el “almacenamiento local de subprocesos” en Python y por qué lo necesito?

En Python específicamente, ¿cómo se comparten las variables entre subprocesos?

Aunque he usado threading.Thread antes, nunca entendí o vi ejemplos de cómo se compartían las variables. ¿Se comparten entre el hilo principal y los niños o solo entre los niños? ¿Cuándo necesitaré usar el almacenamiento local de subprocesos para evitar este intercambio?

He visto muchas advertencias sobre la sincronización del acceso a los datos compartidos entre subprocesos mediante el uso de lockings, pero todavía tengo que ver un buen ejemplo del problema.

¡Gracias por adelantado!

En Python, todo se comparte, excepto las variables de función local (porque cada llamada de función tiene su propio conjunto de locales, y las hebras son siempre llamadas de función separadas). Y aún así, solo las variables en sí (los nombres que se refieren a objetos) son locales a la función; Los objetos mismos son siempre globales, y cualquier cosa puede referirse a ellos. El objeto Thread para un hilo en particular no es un objeto especial a este respecto. Si almacena el objeto Thread algún lugar al que todos los hilos pueden acceder (como una variable global), todos los hilos pueden acceder a ese único objeto Thread . Si desea modificar atómicamente cualquier cosa que no haya creado en este mismo hilo, y no haya almacenado ningún otro hilo, debe protegerlo con un candado. Y, por supuesto, todos los hilos deben compartir este mismo locking, o no sería muy efectivo.

Si desea un almacenamiento real de subprocesos locales, ahí es donde entra en juego threading.local . Los atributos de threading.local no se comparten entre los subprocesos; Cada hilo solo ve los atributos que él mismo coloca allí. Si tiene curiosidad acerca de su implementación, la fuente se encuentra en _threading_local.py en la biblioteca estándar.

Considere el siguiente código:

 #/usr/bin/env python from time import sleep from random import random from threading import Thread, local data = local() def bar(): print("I'm called from", data.v) def foo(): bar() class T(Thread): def run(self): sleep(random()) data.v = self.getName() # Thread-1 and Thread-2 accordingly sleep(1) foo() 
  >> T (). Start ();  T (). Inicio ()
 Me llamo desde el hilo 2
 Me llamo desde el hilo 1 

Aquí threading.local () se usa como una forma rápida y sucia de pasar algunos datos de run () a bar () sin cambiar la interfaz de foo ().

Tenga en cuenta que el uso de variables globales no hará el truco:

 #/usr/bin/env python from time import sleep from random import random from threading import Thread def bar(): global v print("I'm called from", v) def foo(): bar() class T(Thread): def run(self): global v sleep(random()) v = self.getName() # Thread-1 and Thread-2 accordingly sleep(1) foo() 
  >> T (). Start ();  T (). Inicio ()
 Me llamo desde el hilo 2
 Me llamo desde el hilo 2 

Mientras tanto, si pudieras permitirte pasar estos datos como un argumento de foo (), sería una forma más elegante y mejor diseñada:

 from threading import Thread def bar(v): print("I'm called from", v) def foo(v): bar(v) class T(Thread): def run(self): foo(self.getName()) 

Pero esto no siempre es posible cuando se utiliza un código de terceros o mal diseñado.

Puede crear un almacenamiento local de threading.local() utilizando threading.local() .

 >>> tls = threading.local() >>> tls.x = 4 >>> tls.x 4 

Los datos almacenados en el tls serán únicos para cada hilo, lo que ayudará a garantizar que no se produzca el intercambio involuntario.

Al igual que en cualquier otro idioma, cada hilo en Python tiene acceso a las mismas variables. No hay distinción entre el ‘hilo principal’ y los hilos secundarios.

Una diferencia con Python es que el locking global del intérprete significa que solo un hilo puede ejecutar el código Python a la vez. Sin embargo, esto no es de mucha ayuda cuando se trata de sincronizar el acceso, ya que aún se aplican todos los problemas habituales de prevención y tiene que usar primitivas de subprocesos como en otros idiomas. Sin embargo, significa que debe reconsiderar si estaba usando subprocesos para mejorar el rendimiento.