El programa Python se congela cuando la llamada de multiprocesamiento sigue a una llamada secuencial
Encontré un problema extraño con un programa Python que procesaba imágenes con la pyvips
biblioteca. Aquí hay una versión simplificada de mi problema.
Intento crear una matriz numpy (que proviene de ceros en mi ejemplo simple) varias veces (2 veces en el ejemplo).
Cuando lo hago secuencialmente, todo está bien. Cuando uso el multiprocessing
módulo, también funciona como debería. Pero durante mis pruebas noté que el programa se congela cuando realizo estas dos ejecuciones rápidamente, una tras otra . De ahí el programa a continuación. Cuando lo ejecuto, se congela antes del primer "paso 3" en la parte de multiprocesamiento.
Creo que está vinculado a un mecanismo de recolección de basura, pero aquí estoy perdido.
Estoy usando Python 3.11.3 y pyvips 2.2.2 (con vips-8.15.1). Utilizo Spyder con su consola IPython en una máquina Linux (una distribución basada en Ubuntu 22.04).
import multiprocessing
import numpy as np
import pyvips
def myfunction(useless):
print("step 1")
image = pyvips.Image.new_from_array(np.zeros((8, 8, 3)), interpretation="rgb")
print("step 2")
res = np.asarray(image)
print("step 3")
return res
def main(nbpool):
srcrange = range(2)
if nbpool == 0:
res = list()
for srcid in srcrange:
res.append(myfunction(srcid))
return res
else:
pool = multiprocessing.Pool(nbpool)
return pool.map(myfunction, srcrange)
if __name__ == "__main__":
res1 = main(0)
print("Now with multiprocessing")
res2 = main(1)
El problema que está experimentando puede deberse al hecho de que el módulo de multiprocesamiento no funciona bien con las notebooks IPython o Jupyter. Esto se debe a que estos entornos no son seguros para la bifurcación y el multiprocesamiento depende de la capacidad de bifurcar de forma segura el intérprete de Python.
Una forma de resolver potencialmente este problema es utilizar multiprocessing.set_start_method('spawn')
al principio de su secuencia de comandos. Esto hará que el módulo de multiprocesamiento cree un nuevo intérprete de Python para cada proceso hijo, lo que puede evitar problemas con la bifurcación en entornos que no son seguros para la bifurcación.
Así es como puedes modificar tu script:
import multiprocessing
import numpy as np
import pyvips
# Set the start method for multiprocessing
multiprocessing.set_start_method('spawn')
def myfunction(useless):
print("step 1")
image = pyvips.Image.new_from_array(np.zeros((8, 8, 3)), interpretation="rgb")
print("step 2")
res = np.asarray(image)
print("step 3")
return res
def main(nbpool):
srcrange = range(2)
if nbpool == 0:
res = list()
for srcid in srcrange:
res.append(myfunction(srcid))
return res
else:
pool = multiprocessing.Pool(nbpool)
return pool.map(myfunction, srcrange)
if __name__ == "__main__":
res1 = main(0)
print("Now with multiprocessing")
res2 = main(1)
Tenga en cuenta que el uso del método de inicio 'spawn' puede hacer que su programa se ejecute más lento, ya que se debe crear un nuevo intérprete de Python para cada proceso hijo. Sin embargo, puede ayudar a evitar problemas de bifurcación en entornos como portátiles IPython o Jupyter.