¿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, KeyboardInterruptaparece cuando presiono ^C, pero el proceso simplemente se bloquea en ese punto y tengo que eliminarlo externamente.
Quiero poder presionar ^Cen 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 unajoinoperació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
Exceptionclase base se manejan normalmente. Como solución alternativa, puede volver a subir suKeyboardInterruptcomo unaExceptioninstancia:Normalmente obtendría el siguiente resultado:
Entonces, si golpeas
^C, obtendrás:fuente
KeyboardInterruptse llega a mientrasmultiprocessingrealiza su propio intercambio de datos IPC, entoncestry..catchno se activará (obviamente).raise KeyboardInterruptErrorpor areturn. Solo tiene que asegurarse de que el proceso secundario finalice tan pronto como se reciba KeyboardInterrupt. El valor de retorno parece ser ignorado,mainaú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.Poolen una publicación de blog anterior .fuente
os.setpgrp()desde dentro del futuroProcessPoolExecutorno admite funciones de inicializador. En Unix, puede aprovechar laforkestrategia deshabilitando el sighandler en el proceso principal antes de crear el Pool y volver a habilitarlo después. En guijarro , silencioSIGINTen 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 elSIGINTcontrolador 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_asynccon un tiempo de espera en lugar demappara 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
functiones bastante duradera (cientos de segundos).mapy todo está bien.@Linux Cli Aikproporcionó una solución a continuación que produce este comportamiento.map_asyncNo 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_asyncal 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 quemapexiste!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
KeyboardInterruptde los niños. Hubiera esperado que esto funcionara tal como está escrito ... intente cambiarslowly_squarea:Eso debería funcionar como esperabas.
fuente