Локальное хранилище потока полезно, например, если у вас есть рабочий пул потоков, и каждый поток нуждается в доступе к своему собственному ресурсу, такому как соединение с сетью или базой данных. Обратите внимание, что модуль threading
использует обычную концепцию потоков (которые имеют доступ к глобальным данным процесса), но они не слишком полезны из-за глобальной блокировки интерпретатора. Различный модуль multiprocessing
создает новый подпроцесс для каждого, поэтому любой глобальный будет локальным потоком.
резьбовой модуль
Вот простой пример:
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()
Это распечатает:
Nice to meet you MainThread
Welcome back MainThread
Одна важная вещь, которую легко упустить из виду: объект threading.local()
нужно создавать только один раз, а не один раз для потока или для вызова функции. Уровень global
или class
- идеальные места.
Вот почему: threading.local()
фактически создает новый экземпляр каждый раз, когда он вызывается (как любой вызов фабрики или класса), поэтому многократный вызов threading.local()
постоянно перезаписывает исходный объект, что по всей вероятности что хочешь Когда какой-либо поток обращается к существующей переменной threadLocal
(или как она там называется), он получает свое собственное частное представление этой переменной.
Это не будет работать как задумано:
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()
Результатом будет:
First time for MainThread
First time for MainThread
многопроцессорный модуль
Все глобальные переменные являются локальными для потока, поскольку модуль multiprocessing
создает новый процесс для каждого потока.
Рассмотрим этот пример, где счетчик processed
является примером локального хранилища потока:
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)))
Будет выведено что-то вроде этого:
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]
... конечно, идентификаторы потоков и количество для каждого и порядка будут варьироваться от запуска к запуску.