Co to jest "thread local storage" w Pythonie i dlaczego go potrzebuję?

W Pythonie, w jaki sposób zmienne są współdzielone między wątkami?

Chociaż używałem threading.Thread wcześniej tak naprawdę nigdy nie rozumiałem lub nie widziałem przykładów, jak zmienne zostały udostępnione. Czy są one dzielone między głównym wątkiem a dziećmi, czy tylko między dziećmi? Kiedy muszę używać magazynu lokalnego wątku, aby uniknąć tego udostępniania?

Widziałem wiele ostrzeżeń o synchronizacji dostępu do współdzielonych danych między wątkami za pomocą blokad, ale jeszcze nie widziałem naprawdę dobrego przykład problemu.

Z góry dzięki!
Author: Shog9, 2008-09-19

4 answers

W Pythonie wszystko jest współdzielone, z wyjątkiem zmiennych lokalnych funkcji (ponieważ każde wywołanie funkcji otrzymuje własny zestaw lokalnych, a wątki są zawsze oddzielnymi wywołaniami funkcji.) I nawet wtedy, tylko same zmienne (nazwy, które odnoszą się do obiektów) są lokalne dla funkcji; same obiekty są zawsze globalne i wszystko może się do nich odnosić. Obiekt Thread dla określonego wątku nie jest pod tym względem obiektem specjalnym. Jeśli przechowasz obiekt Thread gdzieś, wszystkie wątki mogą access (jak zmienna globalna) wtedy wszystkie wątki mogą uzyskać dostęp do tego obiektu Thread. Jeśli chcesz atomicznie zmodyfikować cokolwiek, czego nie stworzyłeś w tym samym wątku i nie przechowałeś nigdzie innego wątku, musisz zabezpieczyć go za pomocą blokady. I wszystkie wątki muszą oczywiście dzielić ten sam zamek, inaczej nie byłoby to zbyt skuteczne.

Jeśli chcesz mieć rzeczywisty thread - local storage, To właśnie tutaj pojawia się threading.local. Atrybuty threading.local nie są współdzielone pomiędzy wątki; każdy wątek widzi tylko atrybuty, które sam tam umieścił. Jeśli jesteś ciekaw jego implementacji, źródło znajduje się w _threading_local.py w bibliotece standardowej.

 63
Author: Thomas Wouters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 01:16:40

Rozważ następujący kod:

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

Tutaj.local() jest używany jako szybki i brudny sposób przekazywania niektórych danych z run () do bar () bez zmiany interfejsu foo ().

Zauważ, że użycie zmiennych globalnych nie da rady:

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

Tymczasem, gdybyś mógł sobie pozwolić na przekazanie tych danych jako argumentu foo () - byłby to bardziej elegancki i dobrze zaprojektowany sposób: {]}

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())

Ale nie zawsze jest to możliwe w przypadku korzystania z usług osób trzecich lub źle zaprojektowany kod.

 59
Author: ahatchkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-26 01:10:32

Możesz utworzyć lokalny magazyn wątków za pomocą threading.local().

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

Dane przechowywane w tls będą unikalne dla każdego wątku, co pomoże zapewnić, że nie dojdzie do niezamierzonego udostępniania.

 15
Author: Aaron Maenpaa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 01:17:12

Podobnie jak w każdym innym języku, każdy wątek w Pythonie ma dostęp do tych samych zmiennych. Nie ma rozróżnienia między "wątkiem głównym" a wątkami potomnymi.

Jedną z różnic w Pythonie jest to, że globalna Blokada interpretera oznacza, że tylko jeden wątek może uruchamiać kod Pythona na raz. Nie jest to jednak zbyt pomocne, jeśli chodzi o synchronizowanie dostępu, ponieważ wszystkie zwykłe problemy z prewencją nadal mają zastosowanie i musisz używać podstawowych wątków, tak jak w innych językach. Informatyka oznacza to jednak, że musisz rozważyć, czy używasz wątków do wydajności.

 1
Author: Nick Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:03:30