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


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

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

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

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

4 78

4 ответа:

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

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

#/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 

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