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 unterschiedliche multiprocessing
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:
Nice to meet you MainThread
Welcome back MainThread
Eine wichtige Sache, die leicht übersehen wird: Ein threading.local()
Objekt muss nur einmal erstellt werden, nicht einmal pro Thread oder einmal pro Funktionsaufruf. Die global
oder class
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 also threading.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 vorhandene threadLocal
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()
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.