Tengo un módulo de Python que usa multiprocessing. Estoy ejecutando este módulo desde otro script con runpy. Sin embargo, esto da como resultado (1) que el módulo ejecute dos veces y (2) los trabajos multiprocessing nunca finalizan (el script simplemente se cuelga).

En mi ejemplo de trabajo mínimo, tengo un script runpy_test.py :

import runpy
runpy.run_module('module_test')

Y un directorio module_test que contiene un __init__.py vacío y un __main__.py :

from multiprocessing import Pool

print 'start'
def f(x):
    return x*x
pool = Pool()
result = pool.map(f, [1,2,3])
print 'done'

Cuando ejecuto runpy_test.py , obtengo:

start
start

Y el guión se cuelga.

Si elimino la llamada pool.map (o si ejecuto __main__.py directamente, incluida la llamada pool.map), obtengo:

start
done

Estoy ejecutando esto en Scientific Linux 7.6 en Python 2.7.5.

1
user2870171 8 oct. 2019 a las 17:54

3 respuestas

La mejor respuesta

Reescribe tu __main__.py así:

from multiprocessing import Pool
from .implementation import f

print 'start'
pool = Pool()
result = pool.map(f, [1,2,3])
print 'done'

Y luego escriba un implementation.py (puede llamarlo como quiera) en el que se define su función:

def f(x):
    return x*x

De lo contrario, tendrá el mismo problema con la mayoría de las interfaces en el multiprocesamiento, e independientemente del uso de runpy. Como explicó @Weeble, cuando Pool.map intenta cargar la función f en cada subproceso, importará <your_package>.__main__ donde se define su función, pero ya que tiene un código ejecutable a nivel de módulo en __main__ el subproceso lo volverá a ejecutar.

Aparte de esta razón técnica, este también es un mejor diseño en términos de separación de preocupaciones y pruebas. Ahora puede importar y llamar fácilmente (incluso con fines de prueba) la función f sin ejecutarla en paralelo.

1
Iguananaut 8 oct. 2019 a las 15:15

Aunque no es la forma "correcta" de hacerlo, una solución que terminó funcionando para mí fue usar _run_module_as_main de runpy en lugar de run_module. Esto era ideal para mí ya que estaba trabajando con el código de otra persona y requería la menor cantidad de cambios.

0
user2870171 8 oct. 2019 a las 15:55

Intente definir su función f en un módulo separado. Debe ser serializado para pasar a los procesos del grupo, y luego esos procesos deben volver a crearlo, importando el módulo en el que se encuentra. Sin embargo, el archivo __main__.py en el que se encuentra no es un módulo, o al por lo menos, no se porta bien. Intentar importarlo daría como resultado la creación de otro Pool y otra invocación del mapa, lo que parece una receta para el desastre.

1
Weeble 8 oct. 2019 a las 15:19
58288945