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!

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top