In Python, la libreria standard fornisce il modulo threading per la programmazione multithreading. I thread consentono di eseguire più operazioni in parallelo all'interno di un singolo processo. Tuttavia, nella implementazione standard di Python (CPython) esiste un meccanismo chiamato GIL (Global Interpreter Lock) — un blocco globale dell'interprete, che consente solo a un thread di eseguire il bytecode Python alla volta.
Questo significa che in CPython, il multithreading non consente di ottenere un vero parallelismo nell'esecuzione del codice Python a livello della CPU; il parallelismo è utile solo per compiti legati all'attesa (I/O-bound). Per compiti CPU-bound, è consigliabile utilizzare il modulo multiprocessing, che avvia più processi e supera il GIL.
Esempio:
import threading def worker(): print('Inizio') # calcoli pesanti print('Fine') threads = [threading.Thread(target=worker) for _ in range(5)] for t in threads: t.start() for t in threads: t.join()
Per il parallelismo in calcoli intensivi, utilizzare multiprocessing:
from multiprocessing import Pool def square(x): return x*x with Pool(4) as p: print(p.map(square, [1, 2, 3, 4]))
Può il multithreading in Python accelerare l'esecuzione di compiti compute-heavy?
Risposta: No, a causa del GIL nell'implementazione standard di Python (CPython), i thread non forniranno un aumento delle prestazioni per compiti CPU-bound; per tali compiti, utilizzare multiprocessing o implementazioni alternative dell'interprete (come Jython, IronPython), che non hanno GIL.
Storia
In un progetto di elaborazione di grandi volumi di dati, il team ha cercato di accelerare i calcoli utilizzando i thread (
threading). Invece di accelerare, il tempo di esecuzione è aumentato, poiché il GIL impediva ai thread di lavorare in parallelo; dopo essere passato amultiprocessing, il problema è stato risolto.
Storia
Un sviluppatore ha cercato di scaricare grandi file e elaborarli in diversi thread simultaneamente, ma spesso si è imbattuto in un "deadlock" a causa dell'uso imprudente di variabili comuni senza blocchi (thread safety).
Storia
Nel server backend, è stata implementata la gestione delle richieste pesanti in un pool di thread. Con l'aumento del carico, il server ha iniziato a "congelarsi" — si è scoperto che la maggior parte del tempo, i thread trascorrevano in attesa dell'esecuzione del codice Python a causa del GIL, anche se le richieste non erano legate a un intenso I/O.