Uso del grupo de multiprocesamiento python en la terminal y en los módulos de código para Django o Flask
multiprocessing pool (3)
Hay otra fuente posible para este error. Recibí este error al ejecutar el código de ejemplo.
La fuente fue que, a pesar de haber instalado la multiprocesamiento correctamente, el compilador de C ++ no estaba instalado en mi sistema, algo me informó cuando intentaba actualizar el multiprocesamiento. Por lo tanto, podría valer la pena comprobar que el compilador está instalado.
Cuando se utiliza multiproceso. La herramienta en Python con el siguiente código, existe un comportamiento extraño.
from multiprocessing import Pool
p = Pool(3)
def f(x): return x
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
try: print(t.get(timeout=1))
except Exception: pass
Recibo el siguiente error tres veces (uno para cada subproceso en el grupo), e imprime "3" hasta "19":
AttributeError: ''module'' object has no attribute ''f''
Las primeras tres llamadas apply_async nunca regresan.
Mientras tanto, si lo intento:
from multiprocessing import Pool
p = Pool(3)
def f(x): print(x)
p.map(f, range(20))
Obtengo el AttributeError 3 veces, el shell imprime "6" hasta "19", y luego se cuelga y no se puede eliminar con [Ctrl] + [C]
Los documentos de multiprocesamiento tienen lo siguiente para decir:
La funcionalidad dentro de este paquete requiere que el módulo principal sea importable por los niños.
¿Qué significa esto?
Para aclarar, estoy ejecutando código en el terminal para probar la funcionalidad, pero en última instancia quiero poder poner esto en módulos de un servidor web. ¿Cómo se usa correctamente el multiprocesamiento? ¿Trabajo en la terminal de Python y en los módulos de código?
La función que desea ejecutar en un grupo de subprocesos ya debe estar definida cuando cree 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 se crea un grupo, los trabajadores se crean al forzar el proceso actual. Entonces, si la función de destino no está definida en ese momento, el trabajador no podrá llamarla.
En las ventanas es un poco diferente, ya que las ventanas no tienen 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 de 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, al bloquear el programa (o el sistema).
Lo que esto significa es que las agrupaciones deben inicializarse después de las definiciones de funciones que se ejecutarán en ellas. Usar grupos dentro de if __name__ == "__main__":
bloques funcionan si está escribiendo una secuencia de comandos independiente, pero esto no es posible ni en bases de código más grandes ni en el código del servidor (como un proyecto Django o Flask). Entonces, si está tratando de usar Pools en uno de estos, asegúrese de seguir estas pautas, que se explican en las siguientes secciones:
- Inicialice las agrupaciones en la parte inferior de los módulos o en las funciones internas.
- No llame a los métodos de un grupo en el ámbito global de un módulo.
Alternativamente, si solo necesita un mejor paralelismo en la E / S (como los accesos a la base de datos o las 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 como usa subprocesos y no procesos, no tiene ninguna de las advertencias que los grupos de procesos utilizan, con el único inconveniente de que no obtiene un verdadero paralelismo en la ejecución del código. Paralelismo en el bloqueo de E / S.
Los pools deben inicializarse después de las definiciones de las funciones que se ejecutarán en ellos.
El texto inescrutable de los documentos de Python significa que en el momento en que se define el grupo, los hilos del grupo importan el módulo circundante. En el caso de la terminal python, esto significa todo y solo el código que ha ejecutado hasta ahora.
Por lo tanto, 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 el 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, quiero decir bien en Unix. Windows tiene sus propios problemas, que no voy a entrar aquí.
Advertencias al uso de pools en módulos.
¡Pero espera, hay más (para usar grupos en módulos que quieres importar en otro lugar)!
Si define un grupo dentro de una función, no tiene problemas. Pero si está utilizando un objeto Pool como una variable global en un módulo, debe definirse en la parte inferior de la página, no en la parte superior . Aunque esto va en contra del estilo de código más bueno, es necesario para la funcionalidad. La forma de usar un grupo declarado en la parte superior de una página es usarlo solo con las 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 ir 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 ámbito global de un módulo que está importando, el sistema se bloquea . es decir, esto no funciona:
### 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, funciona, siempre y cuando nada importe el módulo 2:
### 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 relacionadas con la razón por la que el código en la pregunta solo escupe un Error de atributo una vez que aparece y luego parece que se ejecuta correctamente. También parece que los hilos de la agrupación (al menos con cierta confiabilidad) vuelven a cargar el código en el módulo después de ejecutar.