¿Cómo puedo manejar los eventos de KeyboardInterrupt con los pools de multiprocesamiento de Python? Aquí hay un ejemplo simple:
from multiprocessing import Pool
from time import sleep
from sys import exit
def slowly_square(i):
sleep(1)
return i*i
def go():
pool = Pool(8)
try:
results = pool.map(slowly_square, range(40))
except KeyboardInterrupt:
# **** THIS PART NEVER EXECUTES. ****
pool.terminate()
print "You cancelled the program!"
sys.exit(1)
print "\nFinally, here are the results: ", results
if __name__ == "__main__":
go()
Cuando ejecuto el código anterior, KeyboardInterrupt
aparece cuando presiono ^C
, pero el proceso simplemente se bloquea en ese punto y tengo que eliminarlo externamente.
Quiero poder presionar ^C
en cualquier momento y hacer que todos los procesos salgan con gracia.
python
multiprocessing
pool
keyboardinterrupt
Fragsworth
fuente
fuente
Respuestas:
Este es un error de Python. Al esperar una condición en threading.Condition.wait (), KeyboardInterrupt nunca se envía. Repro:
La excepción KeyboardInterrupt no se entregará hasta que wait () regrese, y nunca regresa, por lo que la interrupción nunca ocurre. KeyboardInterrupt seguramente debería interrumpir una condición de espera.
Tenga en cuenta que esto no sucede si se especifica un tiempo de espera; cond.wait (1) recibirá la interrupción inmediatamente. Entonces, una solución es especificar un tiempo de espera. Para hacer eso, reemplace
con
o similar.
fuente
Por lo que he encontrado recientemente, la mejor solución es configurar los procesos de trabajo para ignorar SIGINT por completo y limitar todo el código de limpieza al proceso padre. Esto soluciona el problema para los procesos de trabajo inactivo y ocupado, y no requiere código de manejo de errores en sus procesos secundarios.
La explicación y el código de ejemplo completo se pueden encontrar en http://noswap.com/blog/python-multiprocessing-keyboardinterrupt/ y http://github.com/jreese/multiprocessing-keyboardinterrupt respectivamente.
fuente
time.sleep(10)
proceso principal. Si tuviera que eliminar esa suspensión, o si espera hasta que el proceso intente unirse al grupo, lo que tiene que hacer para garantizar que se completen los trabajos, entonces todavía sufre el mismo problema que es el proceso principal. No reciba el KeyboardInterrupt mientras espera unajoin
operación de sondeo .pool.terminate()
nunca se ejecuta. Hacer que los niños ignoren la señal no logra nada. La respuesta de @ Glenn resuelve el problema..join()
excepto en caso de interrupción: simplemente verifica manualmente el resultado del.apply_async()
usoAsyncResult.ready()
para ver si está listo, lo que significa que hemos terminado limpiamente.Por algunas razones, solo las excepciones heredadas de la
Exception
clase base se manejan normalmente. Como solución alternativa, puede volver a subir suKeyboardInterrupt
como unaException
instancia:Normalmente obtendría el siguiente resultado:
Entonces, si golpeas
^C
, obtendrás:fuente
KeyboardInterrupt
se llega a mientrasmultiprocessing
realiza su propio intercambio de datos IPC, entoncestry..catch
no se activará (obviamente).raise KeyboardInterruptError
por areturn
. Solo tiene que asegurarse de que el proceso secundario finalice tan pronto como se reciba KeyboardInterrupt. El valor de retorno parece ser ignorado,main
aún así se recibe KeyboardInterrupt.Por lo general, esta estructura simple funciona para Ctrl- Cen Pool:
Como se indicó en algunas publicaciones similares:
Capture la interrupción del teclado en Python sin try-except
fuente
La respuesta votada no aborda el problema central sino un efecto secundario similar.
Jesse Noller, el autor de la biblioteca de multiprocesamiento, explica cómo tratar correctamente CTRL + C cuando se usa
multiprocessing.Pool
en una publicación de blog anterior .fuente
os.setpgrp()
desde dentro del futuroProcessPoolExecutor
no admite funciones de inicializador. En Unix, puede aprovechar lafork
estrategia deshabilitando el sighandler en el proceso principal antes de crear el Pool y volver a habilitarlo después. En guijarro , silencioSIGINT
en los procesos secundarios de forma predeterminada. No conozco la razón por la que no hacen lo mismo con los Python Pools. Al final, el usuario podría volver a configurar elSIGINT
controlador en caso de que quiera lastimarse.Parece que hay dos problemas que hacen excepciones mientras que el multiprocesamiento es molesto. El primero (señalado por Glenn) es que debe usar
map_async
con un tiempo de espera en lugar demap
para obtener una respuesta inmediata (es decir, no termine de procesar la lista completa). El segundo (señalado por Andrey) es que el multiprocesamiento no captura excepciones que no heredan deException
(por ejemplo,SystemExit
). Así que aquí está mi solución que trata con ambos:fuente
function
es bastante duradera (cientos de segundos).map
y todo está bien.@Linux Cli Aik
proporcionó una solución a continuación que produce este comportamiento.map_async
No siempre se desea usar si el hilo principal depende de los resultados de los procesos secundarios.Descubrí que, por el momento, la mejor solución es no usar la función multiprocessing.pool sino más bien rodar la funcionalidad de su propio pool. Proporcioné un ejemplo que demuestra el error con apply_async, así como un ejemplo que muestra cómo evitar el uso de la funcionalidad del grupo por completo.
http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/
fuente
Soy un novato en Python. Estaba buscando respuestas en todas partes y me topé con este y algunos otros blogs y videos de YouTube. He intentado copiar y pegar el código del autor anterior y reproducirlo en mi Python 2.7.13 en Windows 7 de 64 bits. Está cerca de lo que quiero lograr.
Hice que mi hijo procese para ignorar el ControlC y hacer que el proceso padre finalice. Parece que pasar por alto el proceso secundario me evita este problema.
La parte que comienza en
pool.terminate()
nunca parece ejecutarse.fuente
map_async
al usuario, lo que no me gusta particularmente. En muchas situaciones, como la mía, el hilo principal debe esperar a que finalicen los procesos individuales. ¡Esta es una de las razones por las quemap
existe!Puede intentar usar el método apply_async de un objeto Pool, como este:
Salida:
Una ventaja de este método es que los resultados procesados antes de la interrupción serán devueltos en el diccionario de resultados:
fuente
Por extraño que parezca, también tienes que ocuparte
KeyboardInterrupt
de los niños. Hubiera esperado que esto funcionara tal como está escrito ... intente cambiarslowly_square
a:Eso debería funcionar como esperabas.
fuente