Ctrl-C cioè KeyboardInterrupt per uccidere thread in Python
-
30-09-2019 - |
Domanda
Ho letto da qualche parte che fa eccezione KeyboardInterrupt
viene generato solo nel thread principale in Python. Ho anche letto che il thread principale è bloccata, mentre l'esecuzione della filettatura bambino. Quindi, questo significa che CTRL + C non potrà mai raggiungere il thread figlio. Ho provato il seguente codice:
def main():
try:
thread = threading.Thread(target=f)
thread.start() # thread is totally blocking (e.g., while True)
thread.join()
except KeyboardInterrupt:
print "Ctrl+C pressed..."
sys.exit(1)
def f():
while True:
pass # do the actual work
In questo caso non v'è alcun effetto di CTRL + C sull'esecuzione. E 'come se non è in grado di ascoltare il segnale. Sto capendo questo modo sbagliato? C'è un altro modo per uccidere il filo con CTRL + C ?
Soluzione
Il problema è che si sta utilizzando thread1.join()
, che farà sì che il vostro programma di aspettare fino a che termina di filo per continuare.
I segnali sarà sempre catturato dal processo principale, perché è quello che riceve i segnali, è il processo che ha filetti.
Farlo come si mostra, si sono fondamentalmente esegue un'applicazione 'normale', senza le funzioni di filo, come si avvia 1 filo e attendere fino a quando finisce per continuare.
Altri suggerimenti
Se si desidera avere thread principale per ricevere il CTRL + C segnale, mentre l'adesione, può essere fatto con l'aggiunta di timeout di chiamata join()
.
Di seguito sembra funzionare (non dimenticare di aggiungere daemon=True
se si vuole principale di porre fine in realtà):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
In Python, è vero che le eccezioni KeyboardInterrupt
sono sollevate solo nel thread principale di ciascun processo. Ma, come altre risposte menzionate, è anche vero che i blocchi Thread.join
metodo il thread chiamante, tra cui KeyboardInterrupt
eccezioni . Ecco perché Ctrl + C sembra avere alcun effetto. All'esecuzione principali resti filo bloccati alla linea thread.join()
Quindi, una soluzione semplice per la tua domanda è quello in primo luogo, aggiungere un argomento timeout per thread.join()
e mettere quella chiamata in un ciclo che termina quando il thread bambino, in modo che le eccezioni KeyboardInterrupt
possono essere formulati dopo ogni timeout, e in secondo luogo, rendono il thread figlio demoniaco , il che significa che il suo padre (il thread principale qui) ucciderà quando esce (solo le discussioni non-daemon non vengono uccisi, ma uniti quando le loro uscite genitore):
def main():
try:
thread = threading.Thread(target=f, daemon=True) # create a daemon child thread
thread.start()
while thread.is_alive():
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
Ma una soluzione migliore, se controlli il codice del thread figlio, è quello di informare il thread figlio per uscire con garbo (invece che di colpo come con la prima soluzione), per esempio con un 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