¿Cómo funcionan los subprocesos en Python y cuáles son las trampas específicas de los subprocesos de Python?

He estado tratando de comprender cómo funcionan los hilos en Python, y es difícil encontrar buena información sobre cómo funcionan. Puede que solo me falte un enlace o algo, pero parece que la documentación oficial no es muy completa sobre el tema y no he podido encontrar una buena reseña.

Por lo que puedo decir, solo un hilo puede ejecutarse a la vez, y el hilo activo cambia cada 10 instrucciones o algo así?

¿Dónde hay una buena explicación, o puede proporcionar una? También sería muy bueno estar al tanto de los problemas comunes con los que te topas mientras usas hilos con Python.

Sí, debido al Bloqueo global de intérpretes (GIL) solo puede ejecutar un subproceso a la vez. Aquí hay algunos enlaces con algunas ideas sobre esto:

Desde el último enlace una cita interesante:

Déjame explicarte lo que todo eso significa. Los subprocesos se ejecutan dentro de la misma máquina virtual y, por lo tanto, se ejecutan en la misma máquina física. Los procesos pueden ejecutarse en la misma máquina física o en otra máquina física. Si diseñas tu aplicación alrededor de hilos, no has hecho nada para acceder a varias máquinas. Por lo tanto, puede escalar a la mayor cantidad de núcleos que haya en la única máquina (que serán bastantes a lo largo del tiempo), pero para alcanzar realmente las escalas web, tendrá que resolver el problema de múltiples máquinas de todos modos.

Si desea utilizar varios núcleos, el proceso de pirateo define una API basada en procesos para realizar una paralelización real. El PEP también incluye algunos puntos de referencia interesantes.

Python es un lenguaje bastante fácil de incluir, pero hay advertencias. Lo más importante que debe saber es el locking global de intérpretes. Esto permite que solo un hilo acceda al intérprete. Esto significa dos cosas: 1) rara vez te encuentras usando una statement de locking en Python y 2) si quieres aprovechar los sistemas multiprocesador, debes usar procesos separados. EDITAR: También debo señalar que puede poner parte del código en C / C ++ si también desea desplazarse por la GIL.

Por lo tanto, debe volver a considerar por qué quiere usar hilos. Si desea paralelizar su aplicación para aprovechar la architecture de doble núcleo, debe considerar dividir su aplicación en múltiples procesos.

Si desea mejorar la capacidad de respuesta, debe CONSIDERAR el uso de subprocesos. Sin embargo, hay otras alternativas, a saber, microtapado . También hay algunos marcos que debes considerar:

  • python sin stack
  • Greenlets
  • gevent
  • monóculo

A continuación se muestra una muestra básica de subprocesos. Generará 20 hilos; Cada hilo dará su número de hilo. Ejecutarlo y observar el orden en que se imprimen.

import threading class Foo (threading.Thread): def __init__(self,x): self.__x = x threading.Thread.__init__(self) def run (self): print str(self.__x) for x in xrange(20): Foo(x).start() 

Como ha sugerido, los hilos de Python se implementan a través de la división de tiempo. Así es como consiguen el efecto “paralelo”.

En mi ejemplo, mi clase Foo extiende el hilo, luego implemento el método de run , que es donde va el código que desea ejecutar en un hilo. Para iniciar el subproceso que llama a start() en el objeto de subproceso, que invocará automáticamente el método de run

Por supuesto, esto es sólo lo más básico. Eventualmente querrá aprender acerca de los semáforos, mutexes y lockings para la sincronización de hilos y el paso de mensajes.

Use hilos en python si los trabajadores individuales están realizando operaciones de enlace de E / S. Si está tratando de escalar a través de múltiples núcleos en una máquina, encuentre un buen marco de IPC para python o elija un idioma diferente.

Nota: siempre que mencione un thread me refiero específicamente a hilos en Python hasta que se indique explícitamente.

Los subprocesos funcionan de forma un poco diferente en python si viene del fondo C/C++ . En Python, solo un hilo puede estar en ejecución en un momento dado. Esto significa que los subprocesos en Python no pueden aprovechar la potencia de múltiples núcleos de procesamiento ya que, por diseño, no es posible que los subprocesos se ejecuten en paralelo en varios núcleos.

Como la administración de memoria en python no es segura para subprocesos, cada subproceso requiere un acceso exclusivo a las estructuras de datos en el intérprete de python. Este acceso exclusivo se adquiere mediante un mecanismo llamado GIL (locking de interpretación global) .

Why does python use GIL?

Para evitar que múltiples hilos accedan al estado del intérprete simultáneamente y corromper el estado del intérprete.

La idea es cada vez que se ejecuta un subproceso (incluso si es el subproceso principal) , se adquiere un GIL y, después de un intervalo de tiempo predefinido, el subproceso actual libera el GIL y otro subproceso (si existe).

Why not simply remove GIL?

No es que sea imposible eliminar GIL, sino que, al poco tiempo de hacerlo, terminamos poniendo lockings múltiples dentro del intérprete para serializar el acceso, lo que hace que incluso una sola aplicación de subprocesos tenga menos rendimiento.

por lo tanto, el costo de eliminar GIL se ve reducido por el rendimiento reducido de una aplicación de un solo subproceso, que nunca se desea.

So when does thread switching occurs in python?

El cambio de hilo se produce cuando se lanza GIL. Entonces, ¿cuándo se lanza GIL? Hay dos escenarios a tener en cuenta.

Si un subproceso está realizando operaciones de CPU Bound (procesamiento de imagen Ex).

En las versiones más antiguas de python, el cambio de subprocesos solía ocurrir después de un número fijo de instrucciones de python. Por defecto, estaba establecido en 100 Resultó que no es una muy buena política decidir cuándo debe ocurrir el cambio, ya que el tiempo empleado en la ejecución de una sola La instrucción puede ser muy feroz, de milisegundos a incluso un segundo. Por lo tanto, liberar una GIL después de cada 100 instrucciones, independientemente del tiempo que tomen para ejecutarse, es una política deficiente.

En las nuevas versiones, en lugar de utilizar el recuento de instrucciones como una métrica para cambiar el hilo, se utiliza un intervalo de tiempo configurable. El intervalo de cambio predeterminado es de 5 milisegundos. Puede obtener el intervalo de cambio actual utilizando sys.getswitchinterval() . Esto se puede modificar usando sys.setswitchinterval()

Si un subproceso está realizando algunas operaciones enlazadas de IO (acceso al sistema de archivos ex o
red IO)

GIL se libera siempre que el subproceso está a la espera de que se complete una operación de IO.

Which thread to switch to next?

El intérprete no tiene su propio planificador, cuyo hilo se progtwig al final del intervalo es la decisión del sistema operativo. .

Una solución fácil para GIL es el módulo de multiprocesamiento . Se puede usar como reemplazo del módulo de subprocesos, pero utiliza varios procesos de Intérprete en lugar de subprocesos. Debido a esto, hay un poco más de sobrecarga que el subprocesamiento simple para cosas simples, pero le ofrece la ventaja de una paralelización real si la necesita. También se escala fácilmente a múltiples máquinas físicas.

Si necesita una paralelización verdaderamente a gran escala de la que buscaría más adelante, pero si solo desea escalar a todos los núcleos de una computadora o de unas pocas diferentes sin todo el trabajo que implicaría implementar un marco más completo, este es para usted. .

Trate de recordar que la GIL está configurada para sondear de vez en cuando para mostrar la apariencia de múltiples tareas. Esta configuración puede ajustarse con precisión, pero ofrezco la sugerencia de que debería haber trabajo que los subprocesos están haciendo o que muchos cambios de contexto causarán problemas.

Iría tan lejos como para sugerir varios padres en procesadores y tratar de mantener los mismos trabajos en el mismo núcleo (s).