Подтвердить что ты не робот

Потоковое локальное хранилище в Python

Как использовать локальное хранилище потоков в Python?

Связанные

4b9b3361

Ответ 1

Как отмечено в вопросе, Алекс Мартелли дает решение здесь. Эта функция позволяет нам использовать функцию factory для генерации значения по умолчанию для каждого потока.

#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

Ответ 2

Локальное хранилище потоков полезно, например, если у вас есть пул рабочих потоков, и каждому потоку нужен доступ к его собственному ресурсу, например, к сети или подключению к базе данных. Обратите внимание, что модуль 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() фактически создает новый экземпляр каждый раз, когда он вызывается (как и любой factory или вызов класса), поэтому вызов 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]

... конечно, идентификаторы потоков и подсчеты для каждого и порядка будут варьироваться от run to run.

Ответ 3

Потоковое локальное хранилище можно просто рассматривать как пространство имен (со значениями, доступными через нотацию атрибутов). Разница в том, что каждый поток прозрачно получает свой собственный набор атрибутов/значений, так что один поток не видит значения из другого потока.

Как обычный объект, вы можете создать несколько экземпляров 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 является членом класса (и, следовательно, разделяется между потоками).

В том же примере можно было бы использовать переменную экземпляра или локальную переменную, но это не показало бы многого, так как там не было совместного доступа (и диктофон работал бы так же хорошо). Бывают случаи, когда вам понадобится локальное хранилище потоков как переменные экземпляра или локальные переменные, но они, как правило, относительно редки (и довольно тонкие).

Ответ 4

Можно также написать

import threading
mydata = threading.local()
mydata.x = 1

mydata.x будет существовать только в текущем потоке

Ответ 5

Мой способ сделать поток локального хранилища между модулями/файлами. Следующее было протестировано в 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 и т.д.