Python - Comunicazione tra Thread

Ciao, futuri maghi Python! Oggi ci imbarcheremo in un viaggio avventuroso nel mondo della comunicazione tra thread in Python. Non preoccuparti se sei nuovo nella programmazione – sarò la tua guida amichevole, spiegando tutto passo per passo. Allora, immergiamoci!

Python - Inter-thread Communication

Cos'è la Comunicazione tra Thread?

Prima di entrare nei dettagli, capiamo di cosa si tratta la comunicazione tra thread. Immagina di essere in un team che lavora su un grande progetto. Tutti lavorate su parti diverse, ma a volte dovete condividere informazioni o coordinare i vostri sforzi. Exactamente quello che fanno i thread in un programma, e la comunicazione tra thread è il modo in cui "parlano" tra di loro.

L'Oggetto Evento

Iniziamo con uno dei modi più semplici in cui i thread possono comunicare: l'oggetto Evento.

Cos'è un Oggetto Evento?

Un oggetto Evento è come una bandiera che può essere impostata o cancellata. I thread possono attendere che questa bandiera sia impostata prima di procedere. È un po' come aspettare il verde per attraversare la strada.

Come Utilizzare l'Oggetto Evento

Guardiamo un esempio semplice:

import threading
import time

def attenditore(evento):
print("Attenditore: Attendo che l'evento venga impostato...")
evento.wait()
print("Attenditore: L'evento è stato impostato! Posso procedere ora.")

def impostatore(evento):
print("Impostatore: Imposterò l'evento in 3 secondi...")
time.sleep(3)
evento.set()
print("Impostatore: Ho impostato l'evento!")

# Creazione di un oggetto Evento
e = threading.Event()

# Creazione e avvio dei thread
t1 = threading.Thread(target=attenditore, args=(e,))
t2 = threading.Thread(target=impostatore, args=(e,))

t1.start()
t2.start()

# Attesa per il completamento di entrambi i thread
t1.join()
t2.join()

print("Thread principale: Tutto fatto!")

Spezziamo questo down:

  1. Importiamo il modulo threading per creare thread e il modulo time per aggiungere ritardi.
  2. Definiamo due funzioni: attenditore e impostatore.
  3. La funzione attenditore attende che l'evento sia impostato utilizzando evento.wait().
  4. La funzione impostatore attende 3 secondi (simulando qualche lavoro) e poi imposta l'evento utilizzando evento.set().
  5. Creiamo un oggetto Evento e.
  6. Creiamo due thread, uno per ciascuna funzione, passando l'oggetto Evento a entrambi.
  7. Iniziamo entrambi i thread e poi utilizziamo join() per attendere che terminino.

Quando esegui questo, vedrai l'attenditore attendere, poi dopo 3 secondi, l'impostatore imposta l'evento, e l'attenditore procede.

L'Oggetto Condizione

Ora, saliamo di livello e guardiamo l'oggetto Condizione. È come il cugino più sofisticato dell'oggetto Evento.

Cos'è un Oggetto Condizione?

Un oggetto Condizione permette ai thread di attendere che determinate condizioni diventino vere. È come attendere che una persona specifica arrivi a una festa prima di iniziare i giochi.

Come Utilizzare l'Oggetto Condizione

Ecco un esempio di utilizzo di un oggetto Condizione:

import threading
import time
import random

# Risorsa condivisa
item = []
condizione = threading.Condition()

def produttore():
global item
for i in range(5):
time.sleep(random.random())  # Simula tempi di produzione variabili
with condizione:
item.append(f"Oggetto {i}")
print(f"Produttore ha aggiunto Oggetto {i}")
condizione.notify()  # Notifica il consumatore che un oggetto è disponibile

def consumatore():
global item
while True:
with condizione:
while not item:
print("Consumatore attende...")
condizione.wait()
oggetto = item.pop(0)
print(f"Consumatore ha rimosso {oggetto}")
time.sleep(random.random())  # Simula tempi di consumo variabili

# Creazione e avvio dei thread
produttore_thread = threading.Thread(target=produttore)
consumatore_thread = threading.Thread(target=consumatore)

produttore_thread.start()
consumatore_thread.start()

# Attesa per il completamento del produttore
produttore_thread.join()

# Ferma il consumatore (è in un ciclo infinito)
consumatore_thread.daemon = True

Spezziamo questo down:

  1. Creiamo una risorsa condivisa item e un oggetto Condizione.
  2. La funzione produttore aggiunge oggetti alla lista e notifica il consumatore.
  3. La funzione consumatore attende che gli oggetti siano disponibili, poi rimuove e "consuma" gli oggetti.
  4. Utilizziamo with condizione: per acquisire e rilasciare il blocco della condizione automaticamente.
  5. condizione.wait() rilascia il blocco e attende una notifica.
  6. condizione.notify() sveglia un thread in attesa.

Questo esempio dimostra una classica scenario produttore-consumatore, in cui un thread produce oggetti e un altro li consuma.

Confronto tra Oggetti Evento e Condizione

Ecco un rapido confronto tra oggetti Evento e Condizione:

Caratteristica Evento Condizione
Scopo Segnali semplici Sincronizzazione complessa
Stato Binario (impostato/cancellato) Può avere stati multipli
Attesa I thread attendono che l'evento sia impostato I thread attendono condizioni specifiche
Notifica Tutti i thread in attesa vengono notificati Può notificare uno o tutti i thread in attesa
Caso d'uso Scenari "vai/no-vai" semplici Problemi produttore-consumatore, sincronizzazione complessa

Conclusione

Congratulazioni! Hai appena fatto i tuoi primi passi nel mondo della comunicazione tra thread in Python. Abbiamo coperto l'oggetto Evento per segnali semplici e l'oggetto Condizione per sincronizzazioni più complesse.

Ricorda, come imparare qualsiasi nuovo linguaggio, la pratica fa il maestro. Prova a scrivere i tuoi programmi utilizzando questi oggetti. Magari crea un semplice sistema di chat dove i thread rappresentano utenti diversi, o simula un sistema di semafori utilizzando eventi.

La comunicazione tra thread potrebbe sembrare complicata all'inizio, ma con tempo e pratica, sarai in grado di orchestrare thread come un maestro che dirige un'orchestra. Continua a programmare, a imparare e, più importante, a divertirti!

Credits: Image by storyset