Поток локального хранилища в Python
Как мне использовать локальное хранилище потоков в Python?
связанные с
- Что такое "потоковое локальное хранилище" в Python и зачем оно мне нужно? - Эта тема, кажется, больше ориентирована на то, когда переменные используются совместно.
- Эффективный способ определить, находится ли конкретная функция в стеке в Python - Алекс Мартелли дает хорошее решение
4 ответа
Как отмечено в вопросе, Алекс Мартелли дает решение здесь. Эта функция позволяет нам использовать фабричную функцию для генерации значения по умолчанию для каждого потока.
#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
Локальное хранилище потоков полезно, например, если у вас есть рабочий пул потоков, и каждому потоку необходим доступ к своему собственному ресурсу, например, к сети или к соединению с базой данных. Обратите внимание, что 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
counter является примером локального хранилища потока:
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]
... конечно, идентификаторы потоков и количество для каждого и порядок будут варьироваться от запуска к запуску.
Локальное хранилище потока можно просто представить как пространство имен (со значениями, доступными через нотацию атрибута). Разница в том, что каждый поток прозрачно получает свой собственный набор атрибутов / значений, так что один поток не видит значения из другого потока.
Как и обычный объект, вы можете создать несколько threading.local
экземпляры в вашем коде. Это могут быть локальные переменные, члены класса или экземпляра или глобальные переменные. Каждый из них является отдельным пространством имен.
Вот простой пример:
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()
Выход:
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
Обратите внимание, что каждый поток поддерживает свой собственный счетчик, хотя ns
Атрибут является членом класса (и, следовательно, совместно используется потоками).
В этом же примере можно было бы использовать переменную экземпляра или локальную переменную, но это не показывало бы много, так как тогда не было общего доступа (dict работал бы так же хорошо). Есть случаи, когда вам понадобится локальное хранилище в качестве переменных экземпляра или локальных переменных, но они, как правило, относительно редки (и довольно тонки).
Можно также написать
import threading
mydata = threading.local()
mydata.x = 1
mydata.x будет существовать только в текущем потоке
Мой способ сделать поток локального хранилища между модулями / файлами. Следующее было протестировано в 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
В файле A я запускаю поток с целевой функцией в другом модуле / файле.
В fileB я устанавливаю локальную переменную, которую я хочу в этом потоке.
В fileC я обращаюсь к локальной переменной потока текущего потока.
Кроме того, просто распечатайте переменную словаря, чтобы увидеть доступные значения по умолчанию, такие как kwargs, args и т. Д.