Wie verwende ich den lokalen Thread-Speicher in Python?
verbunden
- Was ist "Thread Local Storage" in Python und warum brauche ich es? - Dieser Thread scheint sich mehr darauf zu konzentrieren, wann Variablen gemeinsam genutzt werden.
- Effiziente Methode, um festzustellen, ob sich in Python eine bestimmte Funktion auf dem Stapel befindet - Alex Martelli bietet eine gute Lösung
python
multithreading
thread-local-storage
Casebash
quelle
quelle
import _threading_local as tl\nhelp(tl)
. B. ) lesbar war .</yikes>
Antworten:
Der lokale Thread-Speicher ist beispielsweise nützlich, wenn Sie über einen Thread-Worker-Pool verfügen und jeder Thread Zugriff auf seine eigene Ressource benötigt, z. B. eine Netzwerk- oder Datenbankverbindung. Beachten Sie, dass das
threading
Modul das reguläre Konzept von Threads verwendet (die Zugriff auf die globalen Prozessdaten haben), diese jedoch aufgrund der globalen Interpretersperre nicht allzu nützlich sind. Das unterschiedlichemultiprocessing
Modul erstellt für jeden einen neuen Unterprozess, sodass jeder globale Thread lokal ist.Gewindemodul
Hier ist ein einfaches Beispiel:
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()
Dies wird ausgedruckt:
Eine wichtige Sache, die leicht übersehen wird: Ein
threading.local()
Objekt muss nur einmal erstellt werden, nicht einmal pro Thread oder einmal pro Funktionsaufruf. Dieglobal
oderclass
Ebene sind ideale Standorte.Hier ist der Grund:
threading.local()
Erstellt tatsächlich jedes Mal eine neue Instanz, wenn sie aufgerufen wird (genau wie bei jedem Factory- oder Klassenaufruf). Wenn Sie alsothreading.local()
mehrmals aufrufen, wird das ursprüngliche Objekt ständig überschrieben, was aller Wahrscheinlichkeit nach nicht das ist, was Sie wollen. Wenn ein Thread auf eine vorhandenethreadLocal
Variable zugreift (oder wie auch immer sie genannt wird), erhält er eine eigene private Ansicht dieser Variablen.Dies funktioniert nicht wie beabsichtigt:
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()
Wird zu dieser Ausgabe führen:
First time for MainThread First time for MainThread
Multiprozessor-Modul
Alle globalen Variablen sind threadlokal, da das
multiprocessing
Modul für jeden Thread einen neuen Prozess erstellt.Betrachten Sie dieses Beispiel, in dem der
processed
Zähler ein Beispiel für den lokalen Thread-Speicher ist: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)))
Es wird ungefähr so ausgegeben:
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]
... natürlich variieren die Thread-IDs und die Anzahl für jede und jede Reihenfolge von Lauf zu Lauf.
quelle
Thread-lokaler Speicher kann einfach als Namespace betrachtet werden (mit Werten, auf die über die Attributnotation zugegriffen wird). Der Unterschied besteht darin, dass jeder Thread transparent seine eigenen Attribute / Werte erhält, sodass ein Thread die Werte eines anderen Threads nicht sieht.
Wie bei einem normalen Objekt können Sie mehrere
threading.local
Instanzen in Ihrem Code erstellen . Dies können lokale Variablen, Klassen- oder Instanzmitglieder oder globale Variablen sein. Jeder ist ein separater Namespace.Hier ist ein einfaches Beispiel:
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()
Ausgabe:
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
Beachten Sie, wie jeder Thread seinen eigenen Zähler verwaltet, obwohl das
ns
Attribut ein Klassenmitglied ist (und daher von den Threads gemeinsam genutzt wird).Das gleiche Beispiel hätte eine Instanzvariable oder eine lokale Variable verwenden können, aber das würde nicht viel zeigen, da es dann keine Freigabe gibt (ein Diktat würde genauso gut funktionieren). Es gibt Fälle, in denen Sie threadlokalen Speicher als Instanzvariablen oder lokale Variablen benötigen, diese sind jedoch relativ selten (und ziemlich subtil).
quelle
ns
um ein Klassenmitglied handelt, sollten wir es nicht als verwendenWorker.ns
? Ich bin mir bewusst, dass der aktuelle Code funktioniert, da erself.ns
als Getter das gleiche Ergebnis liefert wieWorker.ns
, aber als Best Practice, die verwirrend erscheint (und in einigen Fällen fehleranfällig sein kann - wenn Sie dies tun,self.ns = ...
wird das Klassenmitglied nicht geändert, sondern ein erstellt neues Feld auf Instanzebene). Was denken Sie?self
ist zu einem gewissen Grad weitgehend eine Frage des Stils, denke ich. Der Vorteil der Verwendungself
besteht darin, dass sie mit Unterklassen funktioniert, bei denen der Klassenname nicht hart codiert wird. OTOH, es hat den Nachteil, dass es möglich ist, die Klassenvariable versehentlich mit einer Instanzvariablen zu beschatten, wie Sie sagen.Wie in der Frage erwähnt, gibt Alex Martelli hier eine Lösung . Mit dieser Funktion können wir eine Factory-Funktion verwenden, um einen Standardwert für jeden Thread zu generieren.
#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
quelle
dict(default=int)
, wodurch die Notwendigkeit eines "ThreadLocalDefaultDict" entfällt.)dict(default=int)
ist, dass derdict()
Konstruktor kwargs aufnimmt und sie dem Diktat hinzufügt. Wenn dies implementiert wäre, könnten die Benutzer keinen Schlüssel mit dem Namen "Standard" angeben. Aber ich denke tatsächlich, dass dies ein kleiner Preis für eine Implementierung ist, wie Sie sie zeigen. Schließlich gibt es andere Möglichkeiten, einem Diktat einen Schlüssel hinzuzufügen.defaultdict
, wenn Sie das meinen. Wenn Sie meinen, dass dies eine ähnliche Schnittstelle hat wie das, was Sie habendefaultdict
SOLLTEN (optionale Positions- und benannte Argumente für die Factory-Funktion bereitstellen: Jedes Mal, wenn Sie einen Rückruf speichern können, sollten Sie in der Lage sein, optional Argumente dafür zu übergeben! -), dann sorta, außer dass ich normalerweise verschiedene Fabriken und Argumente für verschiedene Varnamen verwende UND der Ansatz, den ich gebe, auch unter Python 2.4 gut funktioniert (fragen Sie nicht ...! -).threadlocal = threading.local()
nicht innerhalb derthreadlocal_var()
Funktion befinden, damit er den lokalen Wert für den Thread erhält, der ihn aufruft?Kann auch schreiben
import threading mydata = threading.local() mydata.x = 1
mydata.x existiert nur im aktuellen Thread
quelle
Meine Art, einen lokalen Thread-Speicher über Module / Dateien hinweg zu erstellen . Folgendes wurde in Python 3.5 getestet -
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
In Datei A starte ich einen Thread, der eine Zielfunktion in einem anderen Modul / einer anderen Datei hat.
In Datei B habe ich eine lokale Variable festgelegt, die ich in diesem Thread haben möchte.
In Datei C greife ich auf die lokale Thread-Variable des aktuellen Threads zu.
Drucken Sie außerdem einfach die Variable 'dictionary' aus, damit Sie die verfügbaren Standardwerte wie kwargs, args usw. Sehen können .
quelle