Saltar al contenido

Usando Python multiprocessing Pool en la terminal y en módulos de código para Django o Flask

Solución:

Lo que esto significa es que las agrupaciones deben inicializarse después de las definiciones de funciones que se ejecutarán en ellas. Usar piscinas dentro if __name__ == "__main__": blocks funciona si está escribiendo un script independiente, pero esto no es posible ni en bases de código más grandes ni en código de servidor (como un proyecto Django o Flask). Por lo tanto, si está intentando utilizar Pools en uno de estos, asegúrese de seguir estas pautas, que se explican en las secciones siguientes:

  1. Inicialice Pools en la parte inferior de los módulos o dentro de las funciones.
  2. No llame a los métodos de un Pool en el ámbito global de un módulo.

Alternativamente, si solo necesita un mejor paralelismo en E / S (como accesos a bases de datos o llamadas de red), puede ahorrarse todo este dolor de cabeza y usar grupos de subprocesos en lugar de grupos de procesos. Esto involucra a los completamente indocumentados:

from multiprocessing.pool import ThreadPool

Su interfaz es exactamente la misma que la de Pool, pero dado que usa subprocesos y no procesos, no tiene ninguna de las advertencias que hace el uso de grupos de procesos, con el único inconveniente de que no obtiene un verdadero paralelismo de ejecución de código, solo paralelismo en el bloqueo de E / S.


Las agrupaciones deben inicializarse después de las definiciones de funciones que se ejecutarán en ellas.

El texto inescrutable de los documentos de Python significa que en el momento en que se define el grupo, los subprocesos del grupo importan el módulo circundante. En el caso de la terminal de Python, esto significa todo y solo el código que ha ejecutado hasta ahora.

Entonces, cualquier función que desee utilizar en el grupo debe definirse antes de que se inicialice el grupo. Esto es cierto tanto para el código en un módulo como para el código en la terminal. Las siguientes modificaciones del código en la pregunta funcionarán bien:

from multiprocessing import Pool
def f(x): return x  # FIRST
p = Pool(3) # SECOND
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
    try: print(t.get(timeout=1))
    except Exception: pass

O

from multiprocessing import Pool
def f(x): print(x)  # FIRST
p = Pool(3) # SECOND
p.map(f, range(20))

Por bien, me refiero a bien en Unix. Windows tiene sus propios problemas, en los que no voy a entrar aquí.


Advertencias sobre el uso de piscinas en módulos

¡Pero espere, hay más (para usar grupos en módulos que desea importar en otro lugar)!

Si define un grupo dentro de una función, no tiene problemas. Pero si está utilizando un objeto Pool como variable global en un módulo, debe definirse en el fondo de la página, no la parte superior. Aunque esto va en contra de la mayoría de los buenos estilos de código, es necesario para la funcionalidad. La forma de usar un grupo declarado en la parte superior de una página es usarlo solo con funciones importadas de otros módulos, así:

from multiprocessing import Pool
from other_module import f
p = Pool(3)
p.map(f, range(20))

Importar un grupo preconfigurado desde otro módulo es bastante horrible, ya que la importación debe venir después de lo que quieras ejecutar en él, así:

### module.py ###
from multiprocessing import Pool
POOL = Pool(5)

### module2.py ###
def f(x):
    # Some function
from module import POOL
POOL.map(f, range(10))

Y segundo, si ejecuta algo en el grupo en el alcance global de un módulo que está importando, el sistema se bloquea. es decir, esto no trabaja:

### module.py ###
from multiprocessing import Pool
def f(x): return x
p = Pool(1)
print(p.map(f, range(5)))

### module2.py ###
import module

Esto, sin embargo, lo hace funciona, siempre que nada importe module2:

### module.py ###
from multiprocessing import Pool

def f(x): return x
p = Pool(1)
def run_pool(): print(p.map(f, range(5)))

### module2.py ###
import module
module.run_pool()

Ahora, las razones detrás de esto son solo más extrañas, y probablemente estén relacionadas con la razón por la que el código en la pregunta solo escupe un Error de atributo una vez cada uno y luego parece ejecutar el código correctamente. También parece que los subprocesos del grupo (al menos con cierta confiabilidad) recargan el código en el módulo después de la ejecución.

La función que desea ejecutar en un grupo de subprocesos ya debe estar definida cuando crea el grupo.

Esto debería funcionar:

from multiprocessing import Pool
def f(x): print(x)
if __name__ == '__main__':
    p = Pool(3)
    p.map(f, range(20))

La razón es que (al menos en los sistemas que tienen fork) cuando crea un grupo, los trabajadores se crean bifurcando el proceso actual. Entonces, si la función de destino aún no está definida en ese punto, el trabajador no podrá llamarla.

En Windows es un poco diferente, ya que Windows no tiene fork. Aquí se inician nuevos procesos de trabajo y se importa el módulo principal. Es por eso que en Windows es importante proteger el código en ejecución con un if __name__ == '__main__'. De lo contrario, cada nuevo trabajador volverá a ejecutar el código y, por lo tanto, generará nuevos procesos infinitamente, bloqueando el programa (o el sistema).

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *