Поток локального хранилища в 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 и т. Д.

Другие вопросы по тегам