Python - Sincronizzazione dei Thread
Ciao a tutti, futuri maghi di Python! Oggi ci imbarcheremo in un viaggio avventuroso nel mondo della sincronizzazione dei thread. Immagina di condurre un'orchestra dove ogni musicista è un thread, e devi assicurarti che suonino tutti in armonia. Questo è essenzialmente quel che significa la sincronizzazione dei thread nella programmazione!
Sincronizzazione dei Thread utilizzando Lock
Cominciamo con lo strumento più basilare del nostro set di sincronizzazione: i lock. Pensa a un lock come a un cartello "non disturbare" sulla porta di una stanza d'albergo. Quando un thread acquisisce un lock, è come mettere quel cartello, dicendo agli altri thread: "Ehi, sono impegnato qui dentro!"
Ecco un esempio semplice per illustrare questo concetto:
import threading
import time
# Risorsa condivisa
contatore = 0
# Crea un lock
lock = threading.Lock()
def incrementa():
global contatore
for _ in range(100000):
lock.acquire()
contatore += 1
lock.release()
# Crea due thread
thread1 = threading.Thread(target=incrementa)
thread2 = threading.Thread(target=incrementa)
# Avvia i thread
thread1.start()
thread2.start()
# Attendi che i thread finiscano
thread1.join()
thread2.join()
print(f"Valore finale del contatore: {contatore}")
In questo esempio, abbiamo una risorsa condivisa contatore
che due thread cercano di incrementare. Senza il lock, potremmo ending up con una condizione di corsa, in cui entrambi i thread cercano di incrementare il contatore simultaneamente, portando a risultati potenzialmente errati.
Utilizzando lock.acquire()
prima di modificare il contatore e lock.release()
dopo, ci assicuriamo che solo un thread possa incrementare il contatore alla volta. È come passare un bastone in una staffetta – solo il thread che tiene il bastone (lock) può correre (modificare la risorsa condivisa).
Oggetti Condition per la Sincronizzazione dei Thread in Python
Ora, alziamo il livello della nostra sincronizzazione con gli Oggetti Condition. Questi sono come semafori sofisticati per i nostri thread, permettendo una coordinazione più complessa.
Ecco un esempio di una scenario produttore-consumatore utilizzando un Oggetto Condition:
import threading
import time
import random
# Buffer condiviso
buffer = []
MAX_SIZE = 5
# Crea un oggetto condition
condition = threading.Condition()
def produttore():
global buffer
while True:
with condition:
while len(buffer) == MAX_SIZE:
print("Buffer pieno, il produttore attende...")
condition.wait()
item = random.randint(1, 100)
buffer.append(item)
print(f"Prodotto: {item}")
condition.notify()
time.sleep(random.random())
def consumatore():
global buffer
while True:
with condition:
while len(buffer) == 0:
print("Buffer vuoto, il consumatore attende...")
condition.wait()
item = buffer.pop(0)
print(f"Consumato: {item}")
condition.notify()
time.sleep(random.random())
# Crea thread produttore e consumatore
produttore_thread = threading.Thread(target=produttore)
consumatore_thread = threading.Thread(target=consumatore)
# Avvia i thread
produttore_thread.start()
consumatore_thread.start()
# Lascialo girare per un po'
time.sleep(10)
In questo esempio, abbiamo un produttore che aggiunge elementi a un buffer e un consumatore che li rimuove. L'Oggetto Condition aiuta a coordinare le loro azioni:
- Il produttore attende quando il buffer è pieno.
- Il consumatore attende quando il buffer è vuoto.
- Si notificano a vicenda quando è sicuro procedere.
È come una danza ben coreografata, con l'Oggetto Condition come coreografo!
Sincronizzazione dei thread utilizzando il metodo join()
Il metodo join()
è come dire a un thread di attendere che un altro finisca la sua performance prima di salire sul palco. È un modo semplice ma potente per sincronizzare i thread.
Ecco un esempio:
import threading
import time
def lavoratore(nome, ritardo):
print(f"{nome} iniziando...")
time.sleep(ritardo)
print(f"{nome} terminato!")
# Crea thread
thread1 = threading.Thread(target=lavoratore, args=("Thread 1", 2))
thread2 = threading.Thread(target=lavoratore, args=("Thread 2", 4))
# Avvia thread
thread1.start()
thread2.start()
# Attendi che thread1 finisca
thread1.join()
print("Thread principale attende dopo thread1")
# Attendi che thread2 finisca
thread2.join()
print("Thread principale attende dopo thread2")
print("Tutti i thread hanno terminato!")
In questo esempio, il thread principale avvia due thread lavoratori e poi attende che ciascuno finisca utilizzando join()
. È come un genitore che attende i suoi figli di finire i compiti prima di servire la cena!
Altri Primitivi di Sincronizzazione
Python offre diversi altri strumenti per la sincronizzazione dei thread. Ecco una rapida occhiata ad alcuni di loro:
Primitivo | Descrizione | Caso d'uso |
---|---|---|
Semaphore | Permette un numero limitato di thread di accedere a una risorsa | Gestione di un pool di connessioni al database |
Event | Permette a un thread di segnalare un evento ad altri thread | Segnalare che un compito è completo |
Barrier | Permette a più thread di attendere fino a quando non raggiungono un certo punto | Sincronizzare l'inizio di una gara |
Ecco un esempio rapido utilizzando una Semaphore:
import threading
import time
# Crea una semaphore che permette 2 thread alla volta
semaphore = threading.Semaphore(2)
def lavoratore(nome):
with semaphore:
print(f"{nome} ha acquisito la semaphore")
time.sleep(1)
print(f"{nome} ha rilasciato la semaphore")
# Crea e avvia 5 thread
threads = []
for i in range(5):
thread = threading.Thread(target=lavoratore, args=(f"Thread {i}",))
threads.append(thread)
thread.start()
# Attendi che tutti i thread finiscano
for thread in threads:
thread.join()
print("Tutti i thread hanno terminato!")
In questo esempio, la semaphore agisce come un buttafuori in un club, permettendo a due thread di entrare alla volta. È perfetto per le situazioni in cui devi limitare l'accesso a una risorsa scarsa!
E con questo, ragazzi! Abbiamo esplorato il affascinante mondo della sincronizzazione dei thread in Python. Ricorda, come condurre un'orchestra o coreografare una danza, sincronizzare i thread è tutto una questione di coordinazione e tempistica. Con questi strumenti nel tuo set di programmazione, sei ben preparato per creare programmi Python multi-threaded armoniosi. Continua a praticare, mantieniti curioso e buon coding!
Credits: Image by storyset