web-dev-qa-db-fra.com

Enfilez le stockage local dans Python

Comment utiliser le stockage local de threads en Python?

En relation

59
Casebash

Comme indiqué dans la question, Alex Martelli donne une solution ici . Cette fonction nous permet d'utiliser une fonction d'usine pour générer une valeur par défaut pour chaque thread.

#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
16
Casebash

Le stockage local des threads est utile, par exemple, si vous disposez d'un pool de threads et que chaque thread a besoin d'accéder à sa propre ressource, comme une connexion réseau ou de base de données. Notez que le module threading utilise le concept normal de threads (qui ont accès aux données globales du processus), mais ceux-ci ne sont pas trop utiles en raison du verrouillage de l'interpréteur global. Les différents modules multiprocessing créent un nouveau sous-processus pour chacun, donc tout global sera thread local.

module de filetage

Voici un exemple 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()

Cela imprimera:

Nice to meet you MainThread
Welcome back MainThread

Une chose importante qui est facilement négligée: un objet threading.local() ne doit être créé qu'une seule fois, pas une fois par thread ni une fois par appel de fonction. Le niveau global ou class sont des emplacements idéaux.

Voici pourquoi: threading.local() crée en fait une nouvelle instance à chaque appel (comme tout appel d'usine ou de classe le ferait), donc appeler threading.local() plusieurs fois écrase constamment l'objet d'origine, ce qui selon toute vraisemblance, ce n'est pas ce que l'on veut. Lorsqu'un thread accède à une variable threadLocal existante (ou son nom), il obtient sa propre vue privée de cette variable.

Cela ne fonctionnera pas comme prévu:

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

Se traduira par cette sortie:

First time for MainThread
First time for MainThread

module multiprocesseur

Toutes les variables globales sont locales au thread, car le module multiprocessing crée un nouveau processus pour chaque thread.

Considérez cet exemple, où le compteur processed est un exemple de stockage local de thread:

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 __== '__main__':
    pool = Pool(processes=4)
    print(pool.map(f, range(10)))

Il produira quelque chose comme ceci:

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]

... bien sûr, les ID de threads et les nombres pour chacun et l'ordre varient d'une exécution à l'autre.

97
mbells

Le stockage local de thread peut simplement être considéré comme un espace de noms (avec des valeurs accessibles via la notation d'attribut). La différence est que chaque thread obtient de manière transparente son propre ensemble d'attributs/valeurs, de sorte qu'un thread ne voit pas les valeurs d'un autre thread.

Tout comme un objet ordinaire, vous pouvez créer plusieurs threading.local instances dans votre code. Il peut s'agir de variables locales, de membres de classe ou d'instance ou de variables globales. Chacun est un espace de noms distinct.

Voici un exemple 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()

Production:

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

Notez comment chaque thread conserve son propre compteur, même si l'attribut ns est un membre de classe (et donc partagé entre les threads).

Le même exemple aurait pu utiliser une variable d'instance ou une variable locale, mais cela ne montrerait pas grand-chose, car il n'y a pas de partage alors (un dict fonctionnerait tout aussi bien). Il y a des cas où vous auriez besoin d'un stockage local de thread en tant que variables d'instance ou variables locales, mais ils ont tendance à être relativement rares (et assez subtils).

18
Paul Moore

Peut également écrire

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

mydata.x n'existera que dans le thread actuel

5
Casebash

Ma façon de faire un stockage local de threads à travers les modules/fichiers . Ce qui suit a été testé dans Python 3.5 -

import threading
from threading import current_thread

# fileA.py 
def functionOne:
    thread = Thread(target = fileB.functionTwo)
    thread.start()

#fileB.py
def functionTwo():
    currentThread = threading.current_thread()
    dictionary = currentThread.__dict__
    dictionary["localVar1"] = "store here"   #Thread local Storage
    fileC.function3()

#fileC.py
def function3():
    currentThread = threading.current_thread()
    dictionary = currentThread.__dict__
    print (dictionary["localVar1"])           #Access thread local Storage

Dans fileA, je démarre un thread qui a une fonction cible dans un autre module/fichier.

Dans fileB, j'ai défini une variable locale que je veux dans ce thread.

Dans fileC, j'accède à la variable locale du thread du thread courant.

De plus, il suffit d'imprimer la variable "dictionnaire" pour que vous puissiez voir les valeurs par défaut disponibles, comme kwargs, args, etc. .

1
Shivansh Jagga