threading set_start_method parallelize new how ejemplos python multithreading asynchronous multiprocessing

set_start_method - python pool process



¿Proceso o multiproceso de Python para lo que estoy haciendo? (2)

Soy nuevo en multiprocesamiento en Python y trato de averiguar si debería usar Pool o Process para llamar a dos funciones async. Las dos funciones que tengo hacen curl llamadas y analizar la información en un 2 listas separadas. Dependiendo de la conexión a Internet, cada función puede tomar aproximadamente 4 segundos cada una. Me doy cuenta de que el cuello de botella está en la conexión de ISP y el multiprocesamiento no lo acelerará mucho, pero sería bueno que ambos inicien la sincronización. Además, esta es una gran experiencia de aprendizaje para mí entrar en el multiproceso de Python porque lo usaré más adelante.

He leído multiprocesamiento de Python. Pool: cuándo usar apply, apply_async o map? y fue útil, pero aún tenía mis propias preguntas.

Entonces, una manera de hacerlo es:

def foo(): pass def bar(): pass p1 = Process(target=foo, args=()) p2 = Process(target=bar, args=()) p1.start() p2.start() p1.join() p2.join()

Las preguntas que tengo para esta implementación son: 1) Desde que se unen los bloques hasta que se completa el proceso de llamada ... ¿significa esto que el proceso de p1 debe finalizar antes de que se inicie el proceso de p2? Siempre entendí que .join () es lo mismo que pool.apply () y pool.apply_sync (). Get () donde el proceso principal no puede iniciar otro proceso (tarea) hasta que se complete la ejecución actual.

La otra alternativa sería algo así como:

def foo(): pass def bar(): pass pool = Pool(processes=2) p1 = pool.apply_async(foo) p1 = pool.apply_async(bar)

Las preguntas que tengo para esta implementación serían: 1) ¿Necesito un pool.close (), pool.join ()? 2) ¿Pool.map () los completaría antes de que pudiera obtener resultados? Y si es así, ¿siguen corriendo asincrónicos? 3) ¿Cómo se diferencian pool.apply_async () de hacer cada proceso con pool.apply () 4) ¿Cómo diferiría esto de la implementación anterior con Process?


Como está obteniendo datos de llamadas curl, está vinculado a IO. En tal caso, las grequests pueden ser útiles. En realidad, no se trata de procesos ni hilos, sino corrutinas: hilos ligeros. Esto le permitiría enviar solicitudes HTTP asincrónicamente y luego usar multiprocessing.Pool para acelerar la parte vinculada a la CPU.

1) Desde que se unieron los bloques hasta que se completa el proceso de llamada ... ¿significa esto que el proceso de p1 debe finalizar antes de que se inicie el proceso de p2?

Sí, se llama a p1.join() después de que p1.join() haya retornado, lo que significa que p1 ha finalizado.

1) ¿Necesito un pool.close (), pool.join ()

Podría terminar con procesos huérfanos sin hacer close() y join() (si los procesos se prestan de forma indefinida)

2) ¿Pool.map () los completaría antes de que pudiera obtener resultados? Y si es así, ¿siguen corriendo asincrónicos?

Se ejecutan de forma asíncrona, pero el map() está bloqueado hasta que se completen todas las tareas.

3) ¿Cómo se diferencian pool.apply_async () de hacer cada proceso con pool.apply ()

pool.apply() está bloqueando, por lo que básicamente harías el proceso sincrónicamente.

4) ¿Cómo diferiría esto de la implementación anterior con el Proceso?

Lo más probable es que un trabajador haya terminado con foo antes de aplicar la bar por lo que podría terminar con un solo trabajador haciendo todo el trabajo. Además, si uno de tus trabajadores muere, Pool genera automáticamente uno nuevo (necesitarás volver a aplicar la tarea).

En resumen : prefiero ir con Pool : es perfecto para casos de productores y consumidores y se encarga de toda la lógica de distribución de tareas.


Los dos escenarios que enumeró logran lo mismo pero de maneras ligeramente diferentes.

El primer escenario inicia dos procesos separados (llámalos P1 y P2) e inicia P1 ejecutando foo y P2 corriendo la bar , y luego espera hasta que ambos procesos hayan terminado sus respectivas tareas.

El segundo escenario inicia dos procesos (llámelos Q1 y Q2) y primero inicia foo en Q1 o Q2, y luego inicia la bar en Q1 o Q2. Luego, el código espera hasta que hayan regresado ambas llamadas a la función.

Entonces, el resultado neto es realmente el mismo, pero en el primer caso, se garantiza que ejecutará foo y bar en diferentes procesos.

En cuanto a las preguntas específicas que tuvo sobre concurrencia, el método .join() en un Process efectivamente se bloquea hasta que el proceso haya finalizado, pero debido a que llamó a .start() tanto en P1 como en P2 (en su primer escenario) antes de unirse, entonces ambos procesos se ejecutarán de forma asíncrona. El intérprete, sin embargo, esperará hasta que P1 termine antes de intentar esperar a que P2 termine.

Para sus preguntas sobre el escenario del grupo, técnicamente debería usar pool.close() pero depende de lo que pueda necesitar después (si sale del alcance, no necesita cerrarlo necesariamente). pool.map() es un tipo de animal completamente diferente, ya que distribuye un grupo de argumentos a la misma función (asincrónicamente) a través de los procesos de grupo y luego espera hasta que todas las llamadas a funciones se hayan completado antes de devolver la lista de resultados.