threads threading thread stop manager manage example ejemplos basics python multithreading thread-local-storage

threading - Enrutar el almacenamiento local en Python



threading python ejemplos (4)

También puede escribir

import threading mydata = threading.local() mydata.x = 1

mydata.x solo existirá en el hilo actual

¿Cómo uso el almacenamiento local de subprocesos en Python?

Relacionado


El almacenamiento local de subprocesos simplemente puede considerarse como un espacio de nombres (con valores accedidos mediante la notación de atributos). La diferencia es que cada subproceso obtiene de forma transparente su propio conjunto de atributos / valores, de modo que un subproceso no vea los valores de otro subproceso.

Al igual que un objeto ordinario, puede crear múltiples instancias threading.local en su código. Pueden ser variables locales, miembros de clase o instancia, o variables globales. Cada uno es un espacio de nombres separado.

Aquí hay un ejemplo simple:

import threading class Worker(threading.Thread): ns = threading.local() def run(self): self.ns.val = 0 for i in range(5): self.ns.val += 1 print("Thread:", self.name, "value:", self.ns.val) w1 = Worker() w2 = Worker() w1.start() w2.start() w1.join() w2.join()

Salida:

Thread: Thread-1 value: 1 Thread: Thread-2 value: 1 Thread: Thread-1 value: 2 Thread: Thread-2 value: 2 Thread: Thread-1 value: 3 Thread: Thread-2 value: 3 Thread: Thread-1 value: 4 Thread: Thread-2 value: 4 Thread: Thread-1 value: 5 Thread: Thread-2 value: 5

Observe cómo cada subproceso mantiene su propio contador, aunque el atributo ns sea ​​un miembro de la clase (y, por lo tanto, compartido entre los subprocesos).

El mismo ejemplo podría haber usado una variable de instancia o una variable local, pero eso no mostraría mucho, ya que no hay intercambio entonces (un dict funcionaría igual de bien). Hay casos en los que necesitaría almacenamiento local de subprocesos como variables de instancia o variables locales, pero tienden a ser relativamente raros (y bastante sutiles).


El almacenamiento local de subprocesos es útil, por ejemplo, si tiene un grupo de trabajo de subprocesos y cada subproceso necesita acceso a su propio recurso, como una conexión de red o de base de datos. Tenga en cuenta que el módulo de threading utiliza el concepto habitual de subprocesos (que tienen acceso a los datos globales de proceso), pero estos no son demasiado útiles debido al bloqueo de intérprete global. El diferente módulo de multiprocessing crea un nuevo subproceso para cada uno, por lo que cualquier global será local.

módulo de roscado

Aquí hay un ejemplo simple:

import threading from threading import current_thread threadLocal = threading.local() def hi(): initialized = getattr(threadLocal, ''initialized'', None) if initialized is None: print("Nice to meet you", current_thread().name) threadLocal.initialized = True else: print("Welcome back", current_thread().name) hi(); hi()

Esto se imprimirá:

Nice to meet you MainThread Welcome back MainThread

Una cosa importante que se pasa por alto fácilmente: un objeto threading.local() solo necesita crearse una vez, ni una vez por hilo ni una vez por llamada de función. El global o de class son ubicaciones ideales.

Aquí está el por qué: threading.local() realidad crea una nueva instancia cada vez que se llama (al igual que cualquier fábrica o llamada de clase), por lo que llamar a threading.local() varias veces sobrescribe constantemente el objeto original, lo que con toda probabilidad es no lo que uno quiere Cuando un hilo accede a una variable threadLocal existente (o como se llame), obtiene su propia vista privada de esa variable.

Esto no funcionará según lo previsto:

import threading from threading import current_thread def wont_work(): threadLocal = threading.local() #oops, this creates a new dict each time! initialized = getattr(threadLocal, ''initialized'', None) if initialized is None: print("First time for", current_thread().name) threadLocal.initialized = True else: print("Welcome back", current_thread().name) wont_work(); wont_work()

Producirá esta salida:

First time for MainThread First time for MainThread

módulo de multiprocesamiento

Todas las variables globales son locales de subprocesos, ya que el módulo de multiprocessing crea un nuevo proceso para cada subproceso.

Considere este ejemplo, donde el contador processed es un ejemplo de almacenamiento local de subprocesos:

from multiprocessing import Pool from random import random from time import sleep import os processed=0 def f(x): sleep(random()) global processed processed += 1 print("Processed by %s: %s" % (os.getpid(), processed)) return x*x if __name__ == ''__main__'': pool = Pool(processes=4) print(pool.map(f, range(10)))

Saldrá algo como esto:

Processed by 7636: 1 Processed by 9144: 1 Processed by 5252: 1 Processed by 7636: 2 Processed by 6248: 1 Processed by 5252: 2 Processed by 6248: 2 Processed by 9144: 2 Processed by 7636: 3 Processed by 5252: 3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

... por supuesto, los identificadores de subprocesos y los recuentos para cada orden variarán de ejecución a ejecución.


Como se señaló en la pregunta, Alex Martelli da una solución aquí . Esta función nos permite usar una función de fábrica para generar un valor predeterminado para cada hilo.

#Code originally posted by Alex Martelli #Modified to use standard Python variable name conventions import threading threadlocal = threading.local() def threadlocal_var(varname, factory, *args, **kwargs): v = getattr(threadlocal, varname, None) if v is None: v = factory(*args, **kwargs) setattr(threadlocal, varname, v) return v