Что такое «локальное хранилище потоков» в Python и зачем оно мне?

StackOverflow https://stackoverflow.com/questions/104983

Вопрос

В частности, в Python, как переменные распределяются между потоками?

Хотя я использовал threading.Thread раньше я никогда по-настоящему не понимал и не видел примеров совместного использования переменных.Распределяются ли они между основным потоком и дочерними элементами или только среди дочерних?Когда мне нужно будет использовать локальное хранилище потоков, чтобы избежать такого совместного использования?

Я видел много предупреждений о синхронизации доступа к общим данным между потоками с помощью блокировок, но мне еще предстоит увидеть действительно хороший пример этой проблемы.

Заранее спасибо!

Это было полезно?

Решение

В Python все является общим, за исключением локальных переменных функции (поскольку каждый вызов функции получает свой собственный набор локальных переменных, а потоки всегда являются отдельными вызовами функций). И даже в этом случае только сами переменные (имена, которые ссылаются на объекты) являются локальными для функции;сами объекты всегда глобальны, и на них может ссылаться что угодно.А Thread объект для конкретного потока в этом отношении не является специальным объектом.Если вы храните Thread объект, к которому имеют доступ все потоки (например, глобальная переменная), тогда все потоки могут получить доступ к этому объекту Thread объект.Если вы хотите атомарно изменить что-либо что вы не просто создали в этом самом потоке и не сохранили его нигде, где другой поток может получить к нему доступ, вы должны защитить его блокировкой.И все потоки, конечно, должны использовать одну и ту же блокировку, иначе это будет не очень эффективно.

Если вам нужно настоящее локальное хранилище потоков, вот где threading.local приходит в.Атрибуты threading.local не распределяются между потоками;каждый поток видит только те атрибуты, которые он туда поместил.Если вам интересно узнать о его реализации, источник находится в _threading_local.py в стандартной библиотеке.

Другие советы

Рассмотрим следующий код:

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread, local

data = local()

def bar():
    print("I'm called from", data.v)

def foo():
    bar()

class T(Thread):
    def run(self):
        sleep(random())
        data.v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
 >> T().start(); T().start()
I'm called from Thread-2
I'm called from Thread-1 

Здесь threading.local() используется как быстрый и грязный способ передачи некоторых данных из run() в bar() без изменения интерфейса foo().

Обратите внимание, что использование глобальных переменных не поможет:

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread

def bar():
    global v
    print("I'm called from", v)

def foo():
    bar()

class T(Thread):
    def run(self):
        global v
        sleep(random())
        v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
 >> T().start(); T().start()
I'm called from Thread-2
I'm called from Thread-2 

Между тем, если бы вы могли позволить себе передать эти данные в качестве аргумента foo() - это был бы более элегантный и хорошо продуманный способ:

from threading import Thread

def bar(v):
    print("I'm called from", v)

def foo(v):
    bar(v)

class T(Thread):
    def run(self):
        foo(self.getName())

Но это не всегда возможно при использовании стороннего или плохо спроектированного кода.

Вы можете создать локальное хранилище потоков, используя threading.local().

>>> tls = threading.local()
>>> tls.x = 4 
>>> tls.x
4

Данные, хранящиеся в tls, будут уникальными для каждого потока, что поможет предотвратить непреднамеренное совместное использование.

Как и в любом другом языке, каждый поток в Python имеет доступ к одним и тем же переменным.Нет различия между «основным потоком» и дочерними потоками.

Единственное отличие от Python заключается в том, что глобальная блокировка интерпретатора означает, что только один поток может одновременно выполнять код Python.Однако это не очень помогает, когда дело доходит до синхронизации доступа, поскольку все обычные проблемы приоритетного доступа по-прежнему сохраняются, и вам приходится использовать примитивы потоков, как и в других языках.Однако это означает, что вам нужно пересмотреть, используете ли вы потоки для повышения производительности.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top