- 1 1. Cos’è un Thread in Python?
- 2 2. Comprensione del Global Interpreter Lock (GIL) in Python
- 3 3. Utilizzo Base del Modulo threading in Python
- 4 4. Creazione di un Thread Tramite Sottoclasse della Classe Thread
- 5 5. Sicurezza dei Thread e Sincronizzazione
- 6 6. Thread per Compiti I/O-Bound vs CPU-Bound
- 7 7. Gestione dei Thread
- 8 8. Confronto: Thread vs multiprocessing
- 9 9. Buone Pratiche per il Modulo threading in Python
- 10 10. Conclusione
1. Cos’è un Thread in Python?
Un thread Python è un meccanismo che permette a più task di eseguire simultaneamente all’interno di un programma. Utilizzando i thread, diverse parti del programma possono eseguire concorrentemente senza attendere l’una l’altra, migliorando l’efficienza. In Python, i thread possono essere creati e gestiti utilizzando il modulo threading.
Concetto Base dei Thread
Un thread è un’unità di esecuzione leggera che esegue all’interno di un processo. Più thread possono eseguire indipendentemente all’interno di un singolo processo, abilitando l’esecuzione concorrente. I thread sono particolarmente utili per operazioni I/O (come lettura/scrittura di file e comunicazione di rete) e per migliorare la reattività delle interfacce utente.
Casi d’Uso dei Thread in Python
Ad esempio, quando si crea uno strumento di web scraping, accedere a più pagine web in parallelo può ridurre il tempo di elaborazione complessivo. Allo stesso modo, in applicazioni di elaborazione dati in tempo reale, i thread permettono aggiornamenti in background senza interrompere l’elaborazione principale.

2. Comprensione del Global Interpreter Lock (GIL) in Python
Il Global Interpreter Lock (GIL) è un concetto cruciale nel threading di Python. È un meccanismo che limita l’interprete Python dall’eseguire più di un thread alla volta.
Impatto del GIL
Il GIL impedisce a più thread di eseguire simultaneamente, garantendo la consistenza nella gestione della memoria all’interno di un processo. Tuttavia, questa restrizione limita i vantaggi del multithreading per task CPU-bound (task che richiedono un’elaborazione CPU significativa). Ad esempio, anche se più thread eseguono calcoli complessi, solo un thread esegue alla volta a causa del GIL, risultando in un miglioramento delle prestazioni limitato.
Modi per Aggirare il GIL
Per aggirare le limitazioni del GIL, puoi utilizzare il modulo multiprocessing per parallelizzare i task. Poiché ogni processo in multiprocessing ha il proprio interprete Python indipendente, non è influenzato dal GIL, permettendo un’esecuzione parallela vera.
3. Utilizzo Base del Modulo threading in Python
Il modulo threading è una libreria standard in Python che abilita la creazione e la gestione dei thread. Qui, copriremo il suo utilizzo base.
Creazione ed Esecuzione di un Thread
Per creare un thread, utilizza la classe threading.Thread. Ad esempio, puoi creare ed eseguire un thread come segue:
import threading
import time
def my_function():
time.sleep(2)
print("Thread executed")
# Creating a thread
thread = threading.Thread(target=my_function)
# Starting the thread
thread.start()
# Waiting for the thread to finish
thread.join()
print("Main thread completed")
In questo esempio, un nuovo thread viene creato e esegue my_function in modo asincrono.
Sincronizzazione dei Thread
Per attendere che un thread finisca, utilizza il metodo join(). Questo metodo mette in pausa il thread principale fino al completamento del thread specificato, garantendo la sincronizzazione tra i thread.

4. Creazione di un Thread Tramite Sottoclasse della Classe Thread
Puoi creare un thread personalizzato sottoclassando la classe threading.Thread.
Sottoclassing di Thread
L’esempio seguente dimostra come sottoclassare la classe Thread e sovrascrivere il metodo run() per definire un thread personalizzato.
import threading
import time
class MyThread(threading.Thread):
def run(self):
time.sleep(2)
print("Custom thread executed")
# Creating and running a custom thread
thread = MyThread()
thread.start()
thread.join()
print("Main thread completed")
Vantaggi del Sottoclassing
Il sottoclassing permette di incapsulare il comportamento del thread, rendendo il codice più riutilizzabile. Abilita anche una gestione flessibile dei thread, come l’assegnazione di dati diversi a ciascun thread.
5. Sicurezza dei Thread e Sincronizzazione
Quando più thread accedono alla stessa risorsa, è richiesta la sincronizzazione per mantenere l’integrità dei dati.
Condizione di Gara
Una condizione di gara si verifica quando più thread modificano la stessa risorsa simultaneamente, portando a risultati imprevedibili. Ad esempio, se più thread incrementano una variabile contatore senza una corretta sincronizzazione, il valore finale potrebbe essere errato.
Sincronizzazione con i Lock
Il modulo threading fornisce un oggetto Lock per la sincronizzazione dei thread. L’uso di un Lock garantisce che solo un thread possa accedere a una risorsa alla volta, prevenendo le condizioni di gara.
import threading
counter = 0
lock = threading.Lock()
def increment_counter():
global counter
with lock:
counter += 1
threads = []
for _ in range(100):
thread = threading.Thread(target=increment_counter)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print("Final counter value:", counter)
In questo esempio, il blocco with lock assicura che il contatore venga incrementato in modo sicuro, evitando inconsistenze nei dati.

6. Thread per Compiti I/O-Bound vs CPU-Bound
I thread sono particolarmente efficaci per i compiti I/O-bound, come le operazioni su file e la comunicazione di rete.
Vantaggi dei Thread per Compiti I/O-Bound
I compiti I/O-bound trascorrono una quantità significativa di tempo in stato di attesa. L’uso dei thread per gestire più operazioni I/O contemporaneamente migliora l’efficienza complessiva. Ad esempio, un programma può leggere/scrivere file mentre gestisce simultaneamente la comunicazione di rete, riducendo i tempi di inattività.
Compiti CPU-Bound e multiprocessing
Per i compiti CPU-bound (come calcoli numerici e elaborazione dati), è consigliato utilizzare il modulo multiprocessing invece di threading. Poiché multiprocessing non è influenzato dal Global Interpreter Lock (GIL), consente un utilizzo efficiente di più core CPU.
7. Gestione dei Thread
Ecco alcune tecniche per gestire efficientemente i thread Python.
Assegnare Nomi e Identificare i Thread
Assegnare nomi ai thread semplifica il debug e il logging. È possibile specificare il nome del thread usando l’argomento name di threading.Thread.
import threading
def task():
print(f"Thread {threading.current_thread().name} is running")
thread1 = threading.Thread(target=task, name="Thread1")
thread2 = threading.Thread(target=task, name="Thread2")
thread1.start()
thread2.start()
Verifica dello Stato del Thread
Per verificare se un thread è attualmente in esecuzione, usa il metodo is_alive(). Questo metodo restituisce True se il thread è ancora in esecuzione e False se ha terminato.
import threading
import time
def task():
time.sleep(1)
print("Task completed")
thread = threading.Thread(target=task)
thread.start()
if thread.is_alive():
print("Thread is still running")
else:
print("Thread has finished")
8. Confronto: Thread vs multiprocessing
Comprendere le differenze tra thread e processi aiuta a determinare il caso d’uso appropriato per ciascuno.
Pro e Contro dei Thread
I thread sono leggeri e condividono la memoria all’interno dello stesso processo, rendendoli efficienti per i compiti I/O-bound. Tuttavia, a causa del Global Interpreter Lock (GIL), le loro prestazioni sono limitate per i compiti CPU-bound.
Vantaggi di multiprocessing
Il modulo multiprocessing consente una vera esecuzione parallela assegnando interpreti Python indipendenti a ciascun processo. Questo è vantaggioso per i compiti intensivi di CPU, ma richiede un overhead aggiuntivo per la comunicazione inter-processo.
9. Buone Pratiche per il Modulo threading in Python
Seguire le migliori pratiche nella programmazione multithread garantisce un funzionamento stabile e un debug più semplice.
Terminazione Sicura dei Thread
Evita di terminare forzatamente i thread. Invece, utilizza flag o variabili di condizione per controllare la loro uscita. Inoltre, assicurati che le risorse vengano rilasciate correttamente quando si ferma un thread.
Prevenzione dei Deadlock
Per prevenire i deadlock quando si usano lock per la sincronizzazione dei thread, segui queste linee guida:
- Mantenere un ordine consistente di acquisizione dei lock.
- Minimizzare l’ambito dei lock.
- Utilizzare l’istruzione
withper garantire il rilascio automatico dei lock.
10. Conclusione
Il modulo threading in Python è uno strumento potente per l’esecuzione concorrente. Questa guida ha trattato l’uso di base, l’impatto del GIL, le differenze tra threading e multiprocessing, e le migliori pratiche per la gestione sicura dei thread.
Sebbene i thread siano ideali per i task bound a I/O, è cruciale comprendere il GIL e scegliere l’approccio appropriato per il tuo caso d’uso. Seguendo le migliori pratiche, puoi migliorare le prestazioni e l’affidabilità dei tuoi programmi Python.



![[Guida completa al ciclo for di Python] Padroneggiare le basi fino alle tecniche avanzate](https://www.python.digibeatrix.com/wp-content/uploads/2024/09/96335675153c1bc8803a23cef384fedc-375x375.webp)