Vai al contenuto principale

Guida definitiva: tutorial sul threading in Python

Impara i concetti di base del threading e come usarli con l'aiuto di Python.
Aggiornato 29 set 2026  · 12 min leggi

Scopri con l'IA

ChatGPTClaudePerplexity

Se hai appena iniziato con Python e vuoi approfondire, segui il corso Introduction to Data Science in Python di DataCamp.

grafica

Introduzione

Il threading ti permette di eseguire in concorrenza parti diverse del tuo processo (Fonte: RealPython). Queste parti differenti sono di solito indipendenti e hanno un’unità di esecuzione separata appartenente allo stesso processo. Un processo non è altro che un programma in esecuzione che contiene unità individuali eseguibili in parallelo. Ad esempio, un browser web può essere un processo, un’applicazione che esegue più videocamere contemporaneamente può essere un processo; anche un videogioco è un esempio di processo.

All’interno di un processo entra in gioco il concetto di threading multiplo, o più comunemente multi-threading, in cui più thread lavorano insieme per raggiungere un obiettivo comune. Il vantaggio più importante dell’uso dei thread è che ti permette di eseguire il programma in parallelo.

Capiremo il concetto di threading con un esempio. Immagina di avere un’applicazione che conta il numero di auto che entrano ed escono dal parcheggio di un centro commerciale. Il tuo apparato ha diverse videocamere che monitorano entrate e uscite collegate a un dispositivo centrale. Ogni videocamera avrà un algoritmo per monitorare il flusso di auto, che apparterrà allo stesso processo o programma. Tuttavia, ciascuna videocamera, insieme all’algoritmo su cui viene eseguita, potrebbe far parte di un thread separato. Non solo: anche i frame letti dalla videocamera e l’algoritmo che fa le previsioni sui frame potrebbero essere due thread distinti.

Un altro esempio potrebbe essere un videogioco in cui il processo deve eseguire attività in parallelo come grafica, interazione con l’utente e networking (nei giochi multiplayer), perché deve rimanere sempre reattivo. Per farlo, deve utilizzare il concetto di multi-threading, dove ogni thread è responsabile dell’esecuzione di un compito indipendente e autonomo.

Un thread ha un proprio flusso di esecuzione, il che significa che nel processo possono accadere più cose contemporaneamente.

È importante notare che ogni processo ha almeno un thread, chiamato main thread. Se il tuo programma non definisce esplicitamente alcun thread, avrà comunque il thread principale, cioè il processo stesso.

Se guardi lo schema sopra, c’è un processo che include il thread principale con le variabili globali e tre thread diversi t1, t2 e t3. Ogni thread ha le proprie variabili locali e il proprio flusso di esecuzione del codice. I thread in genere condividono le risorse del processo: ad esempio, le risorse disponibili per il main thread saranno condivise anche tra i tre thread t1, t2 e t3. Tutti i thread avranno accesso alle variabili globali, pur avendo le loro variabili locali.

Esistono tipi diversi di thread:

  • Thread kernel
  • Thread utente
  • Combinazione di thread kernel e utente

Vediamo ora alcuni vantaggi dell’avere il threading nel tuo programma:

  • Il multi-threading consente al programma di velocizzare l’esecuzione, a patto di avere più CPU.

  • Ti permette anche di svolgere altre attività mentre vengono eseguite operazioni di I/O, grazie a più thread o anche al main thread insieme a un singolo thread. Ad esempio, la velocità con cui i frame della videocamera vengono letti e poi elaborati dall’algoritmo sarà gestita da thread diversi. Quindi, l’algoritmo non dovrà attendere che il frame venga acquisito, e la parte di lettura dei frame non dovrà aspettare che l’algoritmo finisca per poter leggere il frame successivo.

  • I thread all’interno dello stesso processo possono condividere memoria e risorse del thread principale.

Sfide del threading

  • Ricorda che Python si basa sull’implementazione CPython, che limita l’esecuzione a un solo thread per volta; quindi, il threading potrebbe non accelerare tutti i compiti. Il motivo principale è il Global Interpreter Lock (GIL).

    Se vuoi saperne di più sul GIL, dai un’occhiata a questo tutorial.

  • Se vuoi velocizzare un compito intensivo per la CPU, il threading potrebbe non essere la soluzione migliore. In tali casi, il multi-processing è spesso più utile.

  • Per eseguire un programma composto da vari thread, è necessario passare rapidamente da un thread all’altro; quindi, la schedulazione va curata nei programmi con molti thread.

  • La condivisione delle risorse può essere un problema, poiché tutti i thread condividono le stesse risorse e la stessa memoria delle variabili globali. Di conseguenza, operazioni svolte in un thread potrebbero causare un errore di memoria in un altro thread, oppure un altro thread potrebbe non ottenere la memoria necessaria a svolgere il proprio compito.

Threading in Python

  • In Python, il modulo di threading è integrato, si chiama threading e può essere importato direttamente.

  • Poiché in Python quasi tutto è rappresentato come oggetto, anche il threading è un oggetto in Python. Un thread è in grado di:

    • contenere dati,
    • essere archiviato in strutture dati come dizionari, liste, set, ecc.,
    • essere passato come parametro a una funzione.

  • Un thread può anche essere eseguito come un processo.

  • Un thread in Python può avere vari stati come:

    • Attesa,
    • Bloccato.

Vediamo ora come implementare il threading in Python.

Modulo thread in Python3

Nota che Python3 è retrocompatibile con il modulo thread, presente in Python 2.7. In Python3, può essere importato come modulo _thread. Vediamo un esempio per capire il modulo _thread.

import _thread #thread module imported
import time #time module

Definiamo una funzione chiamata thread_delay, che prende due parametri in input, cioè il nome del thread e il ritardo. All’interno di questa funzione:

  • Definirai un contatore a zero,
  • poi userai un ciclo while che verrà eseguito per tre volte,
  • all’interno del while inserirai time.sleep() per aggiungere un ritardo; questo ti aiuterà a capire l’esecuzione del thread. Il ritardo sarà in secondi,
  • quindi incrementerai il contatore di 1,
  • per verificare che l’esecuzione del thread proceda correttamente, stamperai il nome del thread e l’istante in cui viene eseguito.
def thread_delay(thread_name, delay):
    count = 0
    while count < 3:
        time.sleep(delay)
        count += 1
        print(thread_name, '-------->', time.time())

Ora, per aggiungere la funzionalità di thread alla funzione sopra o per eseguirla in un thread, userai il metodo start_new_thread, che si trova nel modulo _thread.

Vediamo la docstring del metodo start_new_thread.

?_thread.start_new_thread
Threading in Python Tutorial

Passiamo quindi la funzione thread_delay senza parentesi e i due argomenti, cioè il nome del thread e il ritardo (per visualizzare l’esecuzione del thread, dato che è molto veloce).

_thread.start_new_thread(thread_delay, ('t1', 1))
_thread.start_new_thread(thread_delay, ('t2', 3))
10916



t1 --------> 1588077856.4221454
t1 --------> 1588077857.4418247
t2 --------> 1588077858.422453
t1 --------> 1588077858.4431
t2 --------> 1588077861.4371448
t2 --------> 1588077864.438915

Dal risultato sopra puoi vedere che il thread t1 inizia a eseguire per primo. Nel frattempo, il thread t2 attende poiché ha un ritardo di 3 secondi e, non appena il ritardo termina, t2 viene eseguito; inoltre, il ritardo per t1 è di soli 1 secondo.

Ora cambiamo il ritardo di t2 a 5 secondi, così t2 partirà dopo che t1 avrà terminato l’esecuzione, dato che t1 impiegherà solo 3 secondi per completarla.

_thread.start_new_thread(thread_delay, ('t1', 1))
_thread.start_new_thread(thread_delay, ('t2', 5))
14632



t1 --------> 1588078001.4087512
t1 --------> 1588078002.4272866
t1 --------> 1588078003.432218
t2 --------> 1588078005.4082909
t2 --------> 1588078010.4268317
t2 --------> 1588078015.4283602

Implementare il threading usando il modulo Threading

Useremo lo stesso esempio di sopra, ma questa volta userai il modulo threading invece del modulo _thread.

import threading
import time
def thread_delay(thread_name, delay):
    count = 0
    while count < 3:
        time.sleep(delay)
        count += 1
        print(thread_name, '-------->', time.time())

All’interno del modulo threading c’è una classe Thread, simile nello spirito alla funzione start_new_thread del modulo _thread.

Vediamo la docstring della classe Thread, che accetta diversi parametri come group, target (ad esempio una funzione), args, ecc.

?threading.Thread
Threading in Python Tutorial

Nel costruttore della classe Thread passerai la funzione target thread_delay e gli argomenti di quella funzione.

t1 = threading.Thread(target=thread_delay, args=('t1', 1))
t2 = threading.Thread(target=thread_delay, args=('t2', 3))

Nel modulo threading, per avviare o eseguire il thread si usa il metodo start(), che è semplicemente responsabile dell’esecuzione del thread.

?t1.start
Threading in Python Tutorial
t1.start()
t2.start()
t1 --------> 1588081121.7790291
t1 --------> 1588081122.7962046
t2 --------> 1588081123.7789369
t1 --------> 1588081123.7980876
t2 --------> 1588081126.7961557
t2 --------> 1588081129.7995002

Userai anche il metodo join, che significa attendere finché l’esecuzione di tutti i thread non è completa. Quindi, qualunque codice tu abbia scritto dopo il metodo join verrà eseguito solo quando questi thread saranno terminati.

?t1.join
Threading in Python Tutorial
t1.start()
t2.start()

t1.join()
t2.join()

print("Thread execution is complete!")
t1 --------> 1588081798.7118263
t1 --------> 1588081799.7231226
t2 --------> 1588081800.7129223
t1 --------> 1588081800.730732
t2 --------> 1588081803.7298765
t2 --------> 1588081806.7309563
Thread execution is complete!

Ora calcoliamo il volume di cubo e quadrato usando il concetto di threading, che è piuttosto semplice.

def volume_cube(a):
    print ("Volume of Cube:", a*a*a)
def volume_square(a):
    print ("Volume of Square:", a*a)
t1 = threading.Thread(target=volume_cube, args=(2))
t2 = threading.Thread(target=volume_square, args=(3))
t1.start()
t2.start()

t1.join()
t2.join()

print("Thread execution is complete!")
Thread execution is complete!


Exception in thread Thread-9:
Traceback (most recent call last):
  File "C:\Program Files\Anaconda3\lib\threading.py", line 926, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Anaconda3\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
TypeError: volume_square() argument after * must be an iterable, not int
Exception in thread Thread-8:
Traceback (most recent call last):
  File "C:\Program Files\Anaconda3\lib\threading.py", line 926, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Anaconda3\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
TypeError: volume_cube() argument after * must be an iterable, not int

Come? Appena hai eseguito il metodo start, è comparso un errore. Questo perché il parametro args si aspetta una tupla e, dato che volume_cube e volume_square si aspettano un solo parametro, devi mettere una virgola nel parametro args dopo aver specificato il primo argomento delle funzioni.

t1 = threading.Thread(target=volume_cube, args=(2,))
t2 = threading.Thread(target=volume_square, args=(3,))
t1.start()
t2.start()

t1.join()
t2.join()

print("Thread execution is complete!")
Volume of Cube: 8
Volume of Square: 9
Thread execution is complete!

Vediamo ora come usare il threading come sottoclasse.

Threading come sottoclasse

In questa sezione imparerai a creare una sottoclasse dalla classe Thread, disponibile nel modulo threading, e poi a creare istanze di thread t1 e t2 a partire da quella classe.

Qui userai di nuovo la stessa funzione thread_delay.

class DataCampThread(threading.Thread):
    def __init__(self, name, delay):
        threading.Thread.__init__(self)
        self.name = name
        self.delay = delay


    def run(self):
        print('Starting Thread:', self.name)
        thread_delay(self.name,self.delay)
        print('Execution of Thread:', self.name, 'is complete!')


t1 = DataCampThread('t1', 1)
t2 = DataCampThread('t2', 3)    

t1.start()
t2.start()

t1.join()
t2.join()

print("Thread execution is complete!")
Starting Thread: t1
Starting Thread: t2
t1 --------> 1588089083.4879806
t1 --------> 1588089084.489222
t1 --------> 1588089085.4909666
Execution of Thread: t1 is complete!
t2 --------> 1588089085.4979634
t2 --------> 1588089088.499365
t2 --------> 1588089091.5012596
Execution of Thread: t2 is complete!
Thread execution is complete!

Ora capiamo passo passo il codice sopra:

  • Hai definito una classe DataCampThread e, nell’argomento della classe, hai passato la classe threading.Thread. Lo fai perché vuoi creare una sottoclasse dal modulo threading. In questo modo puoi usare tutti i metodi disponibili nella classe threading.Thread, in modo analogo al concetto di ereditarietà.

  • Poi hai definito il metodo init della classe DataCampThread e hai passato la funzione insieme al ritardo. L’init è un costruttore: in modo simile ai parametri passati alla classe threading.Thread, qui li passi alla classe DataCampThread.

  • Quindi inizializzi il metodo init della classe principale threading.Thread e poi assegni name e delay.

  • Successivamente, definisci il metodo run; ricorda che qui hai definito il metodo start, che sotto il cofano chiama il metodo run. Dato che hai ereditato la classe principale, puoi modificare il metodo run. Quindi, quando chiami il metodo start, qualunque modifica tu abbia fatto in run verrà ora applicata.

Conclusione

Congratulazioni per aver completato il tutorial.

Questo tutorial è stata un’introduzione di base al threading in Python. Tuttavia, il threading è un argomento molto ampio e fondamentale, e alcuni concetti potrebbero non essere stati approfonditi. Sentiti libero di esplorare altri metodi del threading, come active count, current thread, ecc.

Se vuoi saperne di più, consulta anche la documentazione di Python su Thread-based Parallelism.

Se hai appena iniziato con Python e vuoi approfondire, segui il corso Introduction to Data Science in Python di DataCamp.

Argomenti
Python

Corsi Python

Corso

Introduzione a Python

4 ore
7M
Impara le basi dell’analisi dei dati con Python in sole 4 ore. Questo corso online ti introduce all’interfaccia di Python e ai pacchetti più usati.
Vedi i dettagliRight Arrow
Inizia Il Corso
Mostra altroRight Arrow
Correlato

blog

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

blog

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

blog

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

Mostra AltroMostra Altro