Estoy tratando de entender el enhebrado en Python. He visto la documentación y los ejemplos, pero, francamente, muchos ejemplos son demasiado sofisticados y tengo problemas para comprenderlos.
¿Cómo muestra claramente las tareas divididas para subprocesos múltiples?
Respuestas:
Desde que se hizo esta pregunta en 2010, ha habido una simplificación real en cómo hacer subprocesos simples con Python con mapa y grupo .
El siguiente código proviene de un artículo / blog que definitivamente debe consultar (sin afiliación) - Paralelismo en una línea: un mejor modelo para las tareas de subprocesamiento día a día . Resumiré a continuación: termina siendo solo unas pocas líneas de código:
¿Cuál es la versión multiproceso de:
Descripción
Implementación
multiprocessing.dummy
es exactamente lo mismo que el módulo de multiprocesamiento, pero usa subprocesos en su lugar ( una distinción importante : use múltiples procesos para tareas intensivas de CPU; subprocesos para (y durante) E / S ):Y los resultados del momento:
Pasar múltiples argumentos (funciona así solo en Python 3.3 y versiones posteriores ):
Para pasar múltiples matrices:
O para pasar una constante y una matriz:
Si está utilizando una versión anterior de Python, puede pasar varios argumentos a través de esta solución alternativa ).
(Gracias al usuario 136036 por el útil comentario).
fuente
with Pool(8) as p: p.map( *whatever* )
y deshacerse de las líneas de contabilidad.Aquí hay un ejemplo simple: debe probar algunas URL alternativas y devolver el contenido de la primera para responder.
Este es un caso en el que el subproceso se utiliza como una optimización simple: cada subproceso está esperando que una URL se resuelva y responda, para poner su contenido en la cola; cada hilo es un demonio (no mantendrá el proceso si el hilo principal termina, eso es más común que no); el subproceso principal inicia todos los subprocesos, hace un
get
en la cola para esperar hasta que uno de ellos haya hecho un aput
, luego emite los resultados y finaliza (lo que elimina todos los subprocesos que aún puedan estar en ejecución, ya que son subprocesos de daemon).El uso adecuado de los subprocesos en Python está invariablemente conectado a las operaciones de E / S (dado que CPython no usa múltiples núcleos para ejecutar tareas vinculadas a la CPU de todos modos, la única razón para el subproceso no es bloquear el proceso mientras hay que esperar algunas E / S ) Las colas son casi invariablemente la mejor manera de agilizar el trabajo en subprocesos y / o recopilar los resultados del trabajo, por cierto, y son intrínsecamente seguros, por lo que evitan que se preocupe por bloqueos, condiciones, eventos, semáforos y otros inter -proceso de coordinación / conceptos de comunicación.
fuente
join()
método, ya que eso haría que el hilo principal espere hasta que se terminen sin consumir el procesador constantemente comprobando el valor. @Alex: gracias, esto es exactamente lo que necesitaba para entender cómo usar hilos.Queue
nombre del módulo conqueue
. El nombre del método es el mismo.s = q.get()
print s
@ krs013 No necesita eljoin
porque Queue.get () está bloqueando.NOTA : Para la paralelización real en Python, debe usar el módulo de multiprocesamiento para bifurcar múltiples procesos que se ejecutan en paralelo (debido al bloqueo global del intérprete, los hilos de Python proporcionan intercalado, pero de hecho se ejecutan en serie, no en paralelo, y solo son útil cuando se intercalan operaciones de E / S).
Sin embargo, si simplemente está buscando entrelazado (o está haciendo operaciones de E / S que pueden ser paralelas a pesar del bloqueo global del intérprete), entonces el módulo de subprocesos es el lugar para comenzar. Como un ejemplo realmente simple, consideremos el problema de sumar un amplio rango sumando subranges en paralelo:
Tenga en cuenta que lo anterior es un ejemplo muy estúpido, ya que no hace absolutamente nada de E / S y se ejecutará en serie aunque intercalado (con la sobrecarga adicional de cambio de contexto) en CPython debido al bloqueo global del intérprete.
fuente
thread1
se ejecuta hasta que se completa mientras el subproceso principal se bloquea, luego sucede lo mismothread2
, luego el subproceso principal se reanuda e imprime los valores que acumularon.super(SummingThread, self).__init__()
? Como en stackoverflow.com/a/2197625/806988Como otros mencionaron, CPython puede usar hilos solo para las esperas de E / S debido a GIL .
Si desea beneficiarse de múltiples núcleos para tareas vinculadas a la CPU, use multiprocesamiento :
fuente
f
función. Paralelamente, el programa principal ahora solo espera a que salga el proceso,join
avanzando con él. Si la parte principal acaba de salir, el subproceso podría o no ejecutarse hasta su finalización, por lojoin
que siempre se recomienda hacer una .map
función está aquí: stackoverflow.com/a/28463266/2327328Solo una nota: no se requiere una cola para enhebrar.
Este es el ejemplo más simple que podría imaginar que muestra 10 procesos que se ejecutan simultáneamente.
fuente
for
bucle, puede llamarthread.start()
al primer bucle.La respuesta de Alex Martelli me ayudó. Sin embargo, aquí hay una versión modificada que pensé que era más útil (al menos para mí).
Actualizado: funciona tanto en Python 2 como en Python 3
fuente
import Queue ModuleNotFoundError: No module named 'Queue'
estoy Python 3.6.5 ejecución de algunos mensajes mencionan que en Python 3.6.5 Esqueue
pero incluso después de lo cambio, sigue sin funcionarDada una función,
f
enhebra así:Para pasar argumentos a
f
fuente
Thread
objeto se limpia. Ver los documentos . Hay unis_alive()
método que puede usar para verificar un hilo si lo necesita.is_alive
método, pero no pude averiguar cómo aplicarlo al hilo. Traté de asignarthread1=threading.Thread(target=f).start()
y luego verificarlothread1.is_alive()
, perothread1
está llenoNone
, así que no tuve suerte. ¿Sabes si hay alguna otra forma de acceder al hilo?thread1=threading.Thread(target=f)
seguido dethread1.start()
. Entonces puedes hacerthread1.is_alive()
.thread1.is_alive()
retornosFalse
tan pronto como la función salga.Esto me pareció muy útil: crear tantos subprocesos como núcleos y permitirles ejecutar una (gran) cantidad de tareas (en este caso, llamar a un programa de shell):
fuente
Python 3 tiene la facilidad de lanzar tareas paralelas . Esto facilita nuestro trabajo.
Tiene agrupación de subprocesos y agrupación de procesos .
Lo siguiente da una idea:
Ejemplo de ThreadPoolExecutor ( fuente )
ProcessPoolExecutor ( fuente )
fuente
Usando el nuevo y sorprendente módulo concurrent.futures
El enfoque del ejecutor puede parecer familiar para todos aquellos que se han ensuciado las manos con Java antes.
También en una nota al margen: para mantener el universo cuerdo, no olvide cerrar sus grupos / ejecutores si no usa el
with
contexto (lo cual es tan increíble que lo hace por usted)fuente
Para mí, el ejemplo perfecto para enhebrar es monitorear eventos asincrónicos. Mira este código.
Puedes jugar con este código abriendo una sesión de IPython y haciendo algo como:
Espera unos minutos
fuente
La mayoría de la documentación y los tutoriales usan Python
Threading
y elQueue
módulo, y pueden parecer abrumadores para los principiantes.Quizás considere el
concurrent.futures.ThreadPoolExecutor
módulo de Python 3.Combinado con la
with
cláusula y la comprensión de la lista, podría ser un verdadero encanto.fuente
Vi muchos ejemplos aquí donde no se realizaba trabajo real, y en su mayoría estaban vinculados a la CPU. Aquí hay un ejemplo de una tarea vinculada a la CPU que calcula todos los números primos entre 10 millones y 10.05 millones. He usado los cuatro métodos aquí:
Aquí están los resultados en mi máquina de cuatro núcleos Mac OS X
fuente
if __name__ == '__main__':
antes de la llamada principal, de lo contrario la propia e imprime huevas de medición se ha realizado un intento de iniciar un nuevo proceso antes ... .Aquí está el ejemplo muy simple de importación CSV utilizando subprocesos. (La inclusión de la biblioteca puede diferir para diferentes propósitos).
Funciones de ayuda:
Función del conductor:
fuente
Me gustaría contribuir con un ejemplo simple y las explicaciones que he encontrado útiles cuando tuve que abordar este problema yo mismo.
En esta respuesta, encontrará información sobre el GIL de Python (bloqueo global del intérprete) y un ejemplo simple del día a día escrito usando multiprocessing.dummy más algunos puntos de referencia simples.
Bloqueo global de intérpretes (GIL)
Python no permite múltiples subprocesos en el verdadero sentido de la palabra. Tiene un paquete de subprocesos múltiples, pero si desea varios subprocesos para acelerar su código, generalmente no es una buena idea usarlo.
Python tiene una construcción llamada bloqueo global del intérprete (GIL). El GIL se asegura de que solo uno de sus 'hilos' pueda ejecutarse a la vez. Un hilo adquiere el GIL, hace un poco de trabajo, luego pasa el GIL al siguiente hilo.
Esto sucede muy rápido, por lo que para el ojo humano puede parecer que sus hilos se están ejecutando en paralelo, pero en realidad solo se turnan para usar el mismo núcleo de CPU.
Todo este paso de GIL agrega gastos generales a la ejecución. Esto significa que si desea que su código se ejecute más rápido, usar el paquete de subprocesos a menudo no es una buena idea.
Hay razones para usar el paquete de subprocesos de Python. Si desea ejecutar algunas cosas simultáneamente, y la eficiencia no es una preocupación, entonces está totalmente bien y conveniente. O si está ejecutando código que necesita esperar algo (como algunas E / S), entonces podría tener mucho sentido. Pero la biblioteca de subprocesos no le permitirá usar núcleos de CPU adicionales.
El subprocesamiento múltiple se puede subcontratar al sistema operativo (mediante el procesamiento múltiple) y a alguna aplicación externa que llame a su código de Python (por ejemplo, Spark o Hadoop ), o algún código al que llame su código de Python (por ejemplo: podría haga que su código de Python llame a una función C que hace el costoso material de múltiples subprocesos
Por qué esto importa
Porque muchas personas pasan mucho tiempo tratando de encontrar cuellos de botella en su elegante código multiproceso de Python antes de aprender qué es el GIL.
Una vez que esta información es clara, aquí está mi código:
fuente
Aquí hay múltiples subprocesos con un ejemplo simple que será útil. Puede ejecutarlo y comprender fácilmente cómo funciona el subprocesamiento múltiple en Python. Usé un candado para evitar el acceso a otros hilos hasta que los hilos anteriores terminaron su trabajo. Mediante el uso de esta línea de código,
puede permitir una serie de procesos a la vez y mantener el resto de los subprocesos que se ejecutarán más tarde o después de haber terminado los procesos anteriores.
fuente
Con el préstamo de esta publicación , sabemos cómo elegir entre el subprocesamiento múltiple, el multiprocesamiento y el asíncrono /
asyncio
y su uso.Python 3 tiene una nueva biblioteca integrada para concurrencia y paralelismo: concurrent.futures
Entonces demostraré a través de un experimento ejecutar cuatro tareas (es decir,
.sleep()
método) de la siguienteThreading-Pool
manera:Salida:
[ NOTA ]:
multiprocessing
vsthreading
), puede cambiarlaThreadPoolExecutor
aProcessPoolExecutor
.fuente
Ninguna de las soluciones anteriores usaba múltiples núcleos en mi servidor GNU / Linux (donde no tengo derechos de administrador). Simplemente corrieron en un solo núcleo.
Usé la
os.fork
interfaz de nivel inferior para generar múltiples procesos. Este es el código que funcionó para mí:fuente
fuente