Documentación de Python 3.7
También me gustaría destacar la siguiente cita de la documentación de Pythonthreading
:
Detalle de implementación de CPython: en CPython, debido al Bloqueo global del intérprete, solo un subproceso puede ejecutar código Python a la vez (aunque ciertas bibliotecas orientadas al rendimiento pueden superar esta limitación). Si desea que su aplicación haga un mejor uso de los recursos computacionales de las máquinas multinúcleo, se recomienda usar multiprocessing
o concurrent.futures.ProcessPoolExecutor
. Sin embargo, el subproceso sigue siendo un modelo apropiado si desea ejecutar varias tareas vinculadas de E / S simultáneamente.
Esto enlaza con la entradaglobal interpreter lock
del Glosario para la cual se explica que GIL implica que el paralelismo roscado en Python no es adecuado para tareas vinculadas a la CPU :
El mecanismo utilizado por el intérprete de CPython para garantizar que solo un subproceso ejecute el código de bytes de Python a la vez. Esto simplifica la implementación de CPython al hacer que el modelo de objetos (incluidos los tipos incorporados críticos como dict) esté implícitamente seguro contra el acceso concurrente. El bloqueo de todo el intérprete facilita que el intérprete sea multiproceso, a expensas de gran parte del paralelismo que brindan las máquinas multiprocesador.
Sin embargo, algunos módulos de extensión, ya sean estándar o de terceros, están diseñados para liberar el GIL cuando se realizan tareas computacionalmente intensivas, como la compresión o el hash. Además, el GIL siempre se libera al hacer E / S.
Los esfuerzos anteriores para crear un intérprete de "subproceso libre" (uno que bloquea los datos compartidos con una granularidad mucho más fina) no han tenido éxito porque el rendimiento se vio afectado en el caso común de un solo procesador. Se cree que superar este problema de rendimiento haría que la implementación sea mucho más complicada y, por lo tanto, más costosa de mantener.
Esta cita también implica que los dictados y, por lo tanto, la asignación de variables también son seguros para subprocesos como un detalle de implementación de CPython:
A continuación, los documentos del multiprocessing
paquete. explican cómo supera el GIL al generar el proceso al tiempo que expone una interfaz similar a la de threading
:
El multiprocesamiento es un paquete que admite procesos de generación utilizando una API similar al módulo de subprocesos. El paquete de multiprocesamiento ofrece concurrencia local y remota, evitando de manera efectiva el bloqueo global del intérprete mediante el uso de subprocesos en lugar de subprocesos. Debido a esto, el módulo de multiprocesamiento le permite al programador aprovechar al máximo múltiples procesadores en una máquina determinada. Se ejecuta tanto en Unix como en Windows.
Y los documentosconcurrent.futures.ProcessPoolExecutor
explican que se usa multiprocessing
como backend:
La clase ProcessPoolExecutor es una subclase de ejecutor que utiliza un conjunto de procesos para ejecutar llamadas de forma asincrónica. ProcessPoolExecutor utiliza el módulo de multiprocesamiento, que le permite eludir el bloqueo global del intérprete, pero también significa que solo se pueden ejecutar y devolver objetos seleccionables.
que debería contrastarse con la otra clase base ThreadPoolExecutor
que usa hilos en lugar de procesos
ThreadPoolExecutor es una subclase de ejecutor que utiliza un grupo de subprocesos para ejecutar llamadas de forma asincrónica.
de lo cual concluimos que ThreadPoolExecutor
solo es adecuado para tareas vinculadas de E / S, mientras ProcessPoolExecutor
que también puede manejar tareas vinculadas a la CPU.
La siguiente pregunta pregunta por qué existe el GIL en primer lugar: ¿Por qué el Global Interpreter Lock?
Experimentos de proceso vs hilo
En Multiprocessing vs Threading Python he hecho un análisis experimental de proceso vs threads en Python.
Vista previa rápida de los resultados: