Cos'è & # 8220; archiviazione locale thread & # 8221; in Python, e perché ne ho bisogno?
-
01-07-2019 - |
Domanda
In Python, in che modo le variabili vengono condivise tra i thread?
Anche se ho usato threading.Thread
prima di non aver mai veramente capito o visto esempi di come le variabili venivano condivise. Sono condivisi tra il thread principale e i bambini o solo tra i bambini? Quando dovrei utilizzare l'archiviazione locale dei thread per evitare questa condivisione?
Ho visto molti avvertimenti sulla sincronizzazione dell'accesso ai dati condivisi tra i thread utilizzando i blocchi, ma devo ancora vedere un ottimo esempio del problema.
Grazie in anticipo!
Soluzione
In Python, tutto è condiviso, ad eccezione delle variabili funzione-locali (perché ogni chiamata di funzione ottiene il proprio set di locali e i thread sono sempre chiamate di funzione separate). E anche allora, solo le variabili stesse (i nomi che fanno riferimento agli oggetti) sono locali alla funzione; gli oggetti stessi sono sempre globali e qualsiasi cosa può fare riferimento a loro.
L'oggetto Thread
per un particolare thread non è un oggetto speciale in questo senso. Se memorizzi l'oggetto Thread
da qualche parte a cui tutti i thread possono accedere (come una variabile globale), tutti i thread possono accedere a quell'oggetto Thread
. Se vuoi modificare atomicamente qualsiasi cosa che non hai semplicemente creato in questo stesso thread e non hai memorizzato in un punto in cui un altro thread può trovarlo, devi proteggerlo con un lucchetto. E tutti i thread devono ovviamente condividere questo stesso blocco, altrimenti non sarebbe molto efficace.
Se si desidera l'archiviazione locale effettiva del thread, è qui che entra in gioco threading.local
. Gli attributi di threading.local
non sono condivisi tra thread; ogni thread vede solo gli attributi in esso contenuti. Se sei curioso della sua implementazione, la fonte è in _threading_local.py nella libreria standard.
Altri suggerimenti
Considera il seguente codice:
#/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
Qui threading.local () è usato come un modo rapido e sporco per passare alcuni dati da run () a bar () senza cambiare l'interfaccia di foo ().
Nota che l'uso delle variabili globali non ti aiuterà:
#/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
Nel frattempo, se potessi permetterti di passare questi dati come argomento di foo (), sarebbe un modo più elegante e ben progettato:
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())
Ma questo non è sempre possibile quando si utilizza codice di terze parti o mal progettato.
Puoi creare un archivio locale di thread usando threading.local ()
.
>>> tls = threading.local()
>>> tls.x = 4
>>> tls.x
4
I dati memorizzati su tls saranno univoci per ogni thread, il che contribuirà a garantire che non si verifichi una condivisione involontaria.
Proprio come in ogni altra lingua, ogni thread in Python ha accesso alle stesse variabili. Non c'è distinzione tra il "thread principale" e i thread figlio.
Una differenza con Python è che il Global Interpreter Lock significa che un solo thread può eseguire codice Python alla volta. Questo non è di grande aiuto quando si tratta di sincronizzare l'accesso, tuttavia, poiché si applicano ancora i soliti problemi di prelazione e è necessario utilizzare i thread primitivi proprio come in altre lingue. Ciò significa che è necessario riconsiderare se si utilizzano thread per le prestazioni, tuttavia.