Documentazione di Python 3.7
Vorrei anche evidenziare la seguente citazione dalla documentazione di Pythonthreading
:
Dettagli dell'implementazione di CPython: in CPython, a causa del Global Interpreter Lock, solo un thread può eseguire il codice Python alla volta (anche se alcune librerie orientate alle prestazioni potrebbero superare questa limitazione). Se si desidera che l'applicazione utilizzi meglio le risorse computazionali delle macchine multi-core, si consiglia di utilizzare multiprocessing
o concurrent.futures.ProcessPoolExecutor
. Tuttavia, il threading è ancora un modello appropriato se si desidera eseguire più attività associate a I / O contemporaneamente.
Questo si collega alla voceglobal interpreter lock
del Glossario per la quale spiega che il GIL implica che il parallelismo thread in Python non è adatto per le attività legate alla CPU :
Il meccanismo utilizzato dall'interprete CPython per assicurare che solo un thread esegua il bytecode Python alla volta. Ciò semplifica l'implementazione di CPython rendendo il modello a oggetti (compresi i tipi critici incorporati come dict) implicitamente sicuro contro l'accesso simultaneo. Il blocco dell'intero interprete rende più semplice l'interprete multi-thread, a scapito di gran parte del parallelismo offerto dalle macchine multiprocessore.
Tuttavia, alcuni moduli di estensione, standard o di terze parti, sono progettati in modo da rilasciare GIL quando si eseguono attività ad alta intensità computazionale come la compressione o l'hash. Inoltre, il GIL viene sempre rilasciato quando si esegue l'I / O.
Gli sforzi passati per creare un interprete "a thread libero" (uno che blocca i dati condivisi con una granularità molto più fine) non hanno avuto successo perché le prestazioni hanno sofferto nel caso comune di un singolo processore. Si ritiene che il superamento di questo problema di prestazioni renderebbe l'implementazione molto più complicata e quindi più costosa da mantenere.
Questa citazione implica anche che dicts e quindi l'assegnazione variabile sono anche thread-safe come dettaglio di implementazione di CPython:
Successivamente, i documenti per il multiprocessing
pacchetto spiegano come supera il GIL generando il processo mentre espone un'interfaccia simile a quella di threading
:
multiprocessing è un pacchetto che supporta i processi di generazione utilizzando un'API simile al modulo threading. Il pacchetto multiprocessore offre sia la concorrenza locale sia quella remota, facendo da supporto laterale al Global Interpreter Lock utilizzando sottoprocessi anziché thread. Per questo motivo, il modulo multiprocessore consente al programmatore di sfruttare appieno più processori su una determinata macchina. Funziona sia su Unix che su Windows.
E i documenti perconcurrent.futures.ProcessPoolExecutor
spiegare che utilizza multiprocessing
come backend:
La classe ProcessPoolExecutor è una sottoclasse Executor che utilizza un pool di processi per eseguire le chiamate in modo asincrono. ProcessPoolExecutor utilizza il modulo multiprocessing, che consente di aggirare lateralmente il Global Interpreter Lock ma significa anche che è possibile eseguire e restituire solo oggetti selezionabili.
che dovrebbe essere contrastato con l'altra classe di base ThreadPoolExecutor
che utilizza i thread anziché i processi
ThreadPoolExecutor è una sottoclasse Executor che utilizza un pool di thread per eseguire le chiamate in modo asincrono.
da cui concludiamo che ThreadPoolExecutor
è adatto solo per attività associate a I / O, mentre ProcessPoolExecutor
può anche gestire attività associate alla CPU.
La seguente domanda chiede perché il GIL esiste in primo luogo: perché il Global Interpreter Lock?
Esperimenti di processo vs thread
In Multiprocessing vs Threading Python ho fatto un'analisi sperimentale del processo vs thread in Python.
Anteprima rapida dei risultati: