Ctrl-C, por ejemplo, KeyboardInterrupt para matar hilos en Python

Leí en alguna parte que la excepción KeyboardInterrupt solo se genera en el hilo principal de Python. También leí que el hilo principal está bloqueado mientras se ejecuta el hilo secundario. Entonces, ¿significa esto que CTRL + C nunca puede alcanzar el hilo secundario? Probé el siguiente código:

 def main(): try: thread = threading.Thread(target=f) thread.start() # thread is totally blocking (eg, while True) thread.join() except KeyboardInterrupt: print "Ctrl+C pressed..." sys.exit(1) def f(): while True: pass # do the actual work 

En este caso, no hay efecto de CTRL + C en la ejecución. Es como si no fuera capaz de escuchar la señal. ¿Estoy entendiendo esto de la manera incorrecta? ¿Hay alguna otra manera de matar el hilo usando CTRL + C ?

El problema es que estás usando thread1.join() , lo que hará que tu progtwig espere hasta que ese hilo termine para continuar.

Las señales siempre serán capturadas por el proceso principal, porque es el que recibe las señales, es el proceso el que tiene hilos.

Haciéndolo como se muestra, básicamente está ejecutando una aplicación ‘normal’, sin características de subproceso, al comenzar 1 hilo y esperar hasta que termine para continuar.

Si desea que el subproceso principal reciba la señal CTRL + C mientras se une, puede hacerlo agregando un tiempo de espera para la llamada join() .

Lo siguiente parece estar funcionando (no olvides agregar daemon=True si quieres que main termine):

 thread1.start() while True: thread1.join(600) if not thread1.isAlive(): break 

En Python, es cierto que las excepciones de KeyboardInterrupt se generan solo en el hilo principal de cada proceso. Pero como se Thread.join otras respuestas, también es cierto que el método Thread.join bloquea el subproceso de llamada, incluidas las excepciones de KeyboardInterrupt . Es por eso que Ctrl + C parece no tener efecto: la ejecución en el hilo principal permanece bloqueada en la línea thread.join() .

Entonces, una solución simple para su pregunta es agregar un argumento de tiempo de espera a Thread.join y ponerlo en un bucle infinito en el hilo principal para que se puedan Thread.join excepciones de KeyboardInterrup después de cada tiempo de espera, y hacer que el hilo hijo sea demoníaco , lo que significa su padre (el subproceso principal aquí) lo matará cuando salga (solo los subprocesos que no son daemon no se eliminan sino que se unen cuando se cierra su padre):

 def main(): try: thread = threading.Thread(target=f, daemon=True) # create a daemon child thread thread.start() while True: thread.join(1) # join shortly to not block KeyboardInterrupt exceptions except KeyboardInterrupt: print "Ctrl+C pressed..." sys.exit(1) def f(): while True: pass # do the actual work 

Pero una mejor solución, si controla el código del subproceso secundario, es informar al subproceso secundario para que salga correctamente (en lugar de abruptamente como con la primera solución), por ejemplo con un threading.Event . threading.Event :

 def main(): try: event = threading.Event() thread = threading.Thread(target=f, args=(event,)) thread.start() event.wait() # wait forever but without blocking KeyboardInterrupt exceptions except KeyboardInterrupt: print "Ctrl+C pressed..." event.set() # inform the child thread that it should exit sys.exit(1) def f(event): while not event.is_set(): pass # do the actual work