threads semaforos paralelismo manejo hilos entre eliminar ejemplos detener datos comunicacion compartir como python multithreading parallel-processing

semaforos - paralelismo en python



¿Decidir entre subproceso, multiprocesamiento e hilo en Python? (5)

Desactive Shell y deje que el Unix salga para hacer su trabajo:

use iterpipes para envolver el subproceso y luego:

Del sitio de Ted Ziuba

INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM procesos paralelos

O

Gnu Parallel también servirá

Pasas el rato con GIL mientras envías a los chicos de la trastienda para hacer tu trabajo multinúcleo.

Me gustaría paralelizar mi programa Python para que pueda utilizar múltiples procesadores en la máquina en la que se ejecuta. Mi paralelización es muy simple, ya que todos los "hilos" paralelos del programa son independientes y escriben su salida en archivos separados. No necesito los hilos para intercambiar información, pero es imprescindible que sepa cuándo terminan los hilos, ya que algunos pasos de mi tubería dependen de su salida.

La portabilidad es importante, ya que me gustaría que esto se ejecute en cualquier versión de Python en Mac, Linux y Windows. Dadas estas limitaciones, ¿cuál es el módulo de Python más apropiado para implementar esto? Estoy intentando decidir entre subprocesos, subprocesos y multiprocesamiento, que todos parecen proporcionar una funcionalidad relacionada.

Tiene alguna idea sobre esto? Me gustaría la solución más simple que sea portátil.


En un caso similar, opté por procesos separados y el poco de comunicación necesaria a través del socket de red. Es muy portátil y bastante simple de usar con Python, pero probablemente no tanto (en mi caso también tuve otra restricción: la comunicación con otros procesos escritos en C ++).

En su caso, probablemente optaría por el multiproceso, ya que los hilos de python, al menos cuando se usa CPython, no son hilos reales. Bueno, son subprocesos del sistema nativo, pero los módulos C llamados desde Python pueden o no lanzar el GIL y permitir que otros subprocesos los ejecuten al llamar al código de bloqueo.


Para mí, esto es bastante simple:

La opción del subproceso :

subprocess es para ejecutar otros ejecutables --- básicamente es un contenedor alrededor de os.fork() y os.execve() con algún soporte para plomería opcional (configurando los PIPE desde y hacia los subprocesos. (Obviamente, otras comunicaciones entre procesos (IPC) ) se podrían utilizar mecanismos, como sockets, memoria compartida SysV y colas de mensajes, pero se limitará a las interfaces y los canales IPC admitidos por los programas a los que llama).

Comúnmente, uno utiliza el subprocess sincrónica, simplemente llamando a alguna utilidad externa y leyendo su salida o esperando su finalización (tal vez leyendo sus resultados de un archivo temporal, o después de que se haya publicado en alguna base de datos).

Sin embargo, uno puede engendrar cientos de subprocesos y sondearlos. Mi classh utilidad favorita personal hace exactamente eso. La mayor desventaja del módulo de subprocess es que su soporte de E / S generalmente está bloqueando. Hay un borrador de PEP-3145 para arreglarlo en alguna versión futura de Python asyncproc una alternativa asyncproc (Advertencia que conduce directamente a la descarga, no a ningún tipo de documentación ni a README). También descubrí que es relativamente fácil importar fcntl y manipular los Popen archivos Popen PIPE directamente, aunque no sé si esto es portátil para plataformas que no sean UNIX.

subprocess casi no tiene soporte para el manejo de eventos ... aunque puedes usar el módulo de signal y las sencillas señales UNIX / Linux de la vieja escuela, matando tus procesos suavemente, por así decirlo.

La opción de multiproceso :

multiprocessing es para ejecutar funciones dentro de su código existente (Python) con soporte para comunicaciones más flexibles entre esta familia de procesos. En particular, es mejor construir su IPC de multiprocessing alrededor de los objetos Queue del módulo siempre que sea posible, pero también puede usar objetos Event y varias otras características (algunas de ellas, supuestamente, se basan en soporte de mmap en las plataformas donde ese soporte es suficiente).

El módulo de multiprocessing de Python está destinado a proporcionar interfaces y funciones que son muy similares a las de threading tiempo que permite que CPython escale su procesamiento entre múltiples CPU / núcleos a pesar del GIL (Global Interpreter Lock). Aprovecha todo el esfuerzo de coherencia y bloqueo SMP de gran precisión que realizaron los desarrolladores de su kernel OS.

La opción de roscado :

threading es para un rango bastante estrecho de aplicaciones que están vinculadas con E / S (no es necesario escalar en múltiples núcleos de CPU) y que se benefician de la latencia extremadamente baja y la sobrecarga de conmutación de la conmutación de subprocesos (con memoria central compartida) vs. proceso / cambio de contexto. En Linux, este es casi el conjunto vacío (los tiempos de conmutación del proceso Linux son extremadamente cercanos a sus conmutadores de subprocesos).

threading sufre de dos grandes desventajas en Python .

Uno, por supuesto, es específico de la implementación, afectando principalmente a CPython. Ese es el GIL. En su mayor parte, la mayoría de los programas de CPython no se beneficiarán de la disponibilidad de más de dos CPU (núcleos) y, a menudo, el rendimiento sufrirá la contención de bloqueo de GIL.

El problema más amplio que no es específico de la implementación es que los subprocesos comparten la misma memoria, manejadores de señal, descriptores de archivos y ciertos otros recursos del sistema operativo. Por lo tanto, el programador debe tener mucho cuidado con el bloqueo de objetos, el manejo de excepciones y otros aspectos de su código que son sutiles y que pueden matar, detener o bloquear el proceso completo (conjunto de hilos).

En comparación, el modelo de multiprocessing le da a cada proceso su propia memoria, descriptores de archivos, etc. Una excepción bloqueada o no controlada en cualquiera de ellos solo matará ese recurso y manejar robustamente la desaparición de un proceso hijo o hermano puede ser considerablemente más fácil que la depuración, aislando y arreglando o trabajando en torno a problemas similares en los hilos.

  • (Nota: el uso de threading con los principales sistemas de Python, como NumPy , puede sufrir considerablemente menos por la contención de GIL que la mayoría de tu propio código de Python. Esto se debe a que han sido diseñados específicamente para hacerlo).

La opción retorcida :

También vale la pena señalar que Twisted ofrece otra alternativa que es a la vez elegante y muy difícil de entender . Básicamente, a riesgo de simplificar en exceso hasta el punto en que los fanáticos de Twisted pueden asaltar mi casa con horquillas y antorchas, Twisted ofrece multitareas cooperativas impulsadas por eventos dentro de cualquier (único) proceso.

Para entender cómo esto es posible, se debe leer acerca de las características de select() (que se puede construir en torno a select () o poll () o llamadas similares al sistema operativo). Básicamente, todo se debe a la capacidad de solicitar que el sistema operativo duerma en espera de cualquier actividad en una lista de descriptores de archivos o algún tiempo de espera.

El despertar de cada una de estas llamadas para select() es un evento --- ya sea uno que involucra la entrada disponible (legible) en algún número de sockets o descriptores de archivos, o que el espacio de almacenamiento en búfer está disponible en otros descriptores o zócalos (editables), algunos excepcionales condiciones (TCP paquetes fuera de banda PUSH''d, por ejemplo), o un tiempo de espera.

Por lo tanto, el modelo de programación Twisted se basa en el manejo de estos eventos y luego en el controlador "principal" resultante, permitiéndole enviar los eventos a sus manejadores.

Personalmente, pienso en el nombre Twisted como evocador del modelo de programación ... ya que su enfoque del problema debe ser, en cierto sentido, "retorcido" al revés. En lugar de concebir su programa como una serie de operaciones sobre datos de entrada y salidas o resultados, está escribiendo su programa como un servicio o daemon y definiendo cómo reacciona a varios eventos. (De hecho, el "bucle principal" central de un programa Twisted es (generalmente? Siempre?) Un reactor() .

Los principales desafíos para usar Twisted implican torcer su mente en torno al modelo impulsado por eventos y también evitar el uso de bibliotecas de clases o kits de herramientas que no están escritos para cooperar dentro del marco de Twisted. Esta es la razón por la cual Twisted suministra sus propios módulos para el manejo de protocolo SSH, para maldiciones y sus propias funciones de subproceso / popen, y muchos otros módulos y controladores de protocolo que, a primera vista, parecerían duplicar cosas en las bibliotecas estándar de Python.

Creo que es útil entender Twisted en un nivel conceptual, incluso si nunca tiene la intención de usarlo. Puede proporcionar información sobre el rendimiento, la contención y el manejo de eventos en su procesamiento de subprocesos, subprocesos y subprocesos, así como cualquier procesamiento distribuido que realice.

( Nota: las versiones más nuevas de Python 3.x incluyen asyncio (E / S asíncronas) como async def , el decorador @ async.coroutine y la palabra clave await , y ceden de la compatibilidad futura . Todas estas son más o menos similares a Retorcido desde una perspectiva de proceso (multitarea cooperativa).

La opción distribuida :

Sin embargo, otro ámbito de procesamiento sobre el que no ha preguntado, pero que merece la pena considerar, es el del procesamiento distribuido . Existen muchas herramientas y frameworks de Python para el procesamiento distribuido y el cómputo paralelo. Personalmente, creo que el más fácil de usar es el que menos se considera en ese espacio.

Es casi trivial construir un procesamiento distribuido alrededor de Redis . El almacén de claves completo se puede usar para almacenar unidades de trabajo y resultados, los LIST de Redis se pueden usar como objetos similares a Queue() , y el soporte PUB / SUB se puede usar para el manejo de Event . Puede actualizar sus claves y usar valores, replicados en un clúster de instancias de Redis, para almacenar la topología y las asignaciones de tokens y hash para proporcionar un hash y un fail-over consistentes para escalar más allá de la capacidad de cualquier instancia para coordinar a sus trabajadores. y recopilación de datos (en escabeche, JSON, BSON o YAML) entre ellos.

Por supuesto, a medida que comienzas a construir una solución a mayor escala y más sofisticada en Redis, estás implementando muchas de las funciones que ya se han solucionado con Celery , Apache Spark y Hadoop , Zookeeper , etcd , Cassandra , etc. Esos también tienen módulos para el acceso de Python a sus servicios.

[Actualización: un par de recursos para considerar si está considerando Python para intensificar computacionalmente en todos los sistemas distribuidos: IPython Parallel y PySpark . Si bien estos son sistemas de computación distribuida de propósito general, son particularmente accesibles y populares subsistemas de ciencia y análisis de datos.

Conclusión

Allí tiene la gama de alternativas de procesamiento para Python, desde un solo subproceso, con llamadas sincrónicas simples a subprocesos, grupos de subprocesos sondeados, subprocesos enrutados y multiprocesos, multitarea cooperativa impulsada por eventos y hasta el procesamiento distribuido.


Para usar múltiples procesadores en CPython, su única opción es el módulo de multiprocessing . CPython mantiene un bloqueo en su interior (el GIL ) que evita que los hilos en otras CPU funcionen en paralelo. El módulo de multiprocessing crea nuevos procesos (como subprocess ) y gestiona la comunicación entre ellos.


multiprocessing es un gran tipo de módulo de cuchillo suizo-ejército. Es más general que los hilos, ya que incluso puede realizar cálculos remotos. Este es, por lo tanto, el módulo que sugiero que use.

El módulo de subprocess también le permitirá iniciar múltiples procesos, pero me pareció menos conveniente de usar que el nuevo módulo de multiprocesamiento.

Los hilos son notoriamente sutiles y, con CPython, a menudo están limitados a un núcleo, con ellos (aunque, como se señala en uno de los comentarios, el bloqueo de intérprete global (GIL) se puede liberar en código C llamado desde el código de Python) .

Creo que la mayoría de las funciones de los tres módulos que cita pueden usarse de una manera independiente de la plataforma. En el lado de la portabilidad, tenga en cuenta que el multiprocessing solo viene en forma estándar desde Python 2.6 (aunque existe una versión para algunas versiones anteriores de Python). ¡Pero es un gran módulo!