Ctrl-C-à-dire KeyboardInterrupt pour tuer des fils en python
-
30-09-2019 - |
Question
J'ai lu quelque part que exception KeyboardInterrupt
ne se pose dans le thread principal en Python. Je lis aussi que le thread principal est bloqué alors que les exécute de fil de l'enfant. Alors, est-ce que cela signifie que CTRL + C ne peut jamais atteindre au fil de l'enfant. J'ai essayé le code suivant:
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
Dans ce cas, il n'y a pas d'effet de CTRL + C sur l'exécution. Il est comme il ne peut pas écouter le signal. Est-ce que je comprends ce dans le mauvais sens? Est-il un autre moyen de tuer le fil en utilisant CTRL + C ?
La solution
Le problème est que vous utilisez thread1.join()
, ce qui fera de votre programme d'attendre jusqu'à ce que fin du thread pour continuer.
Les signaux seront toujours pris par le processus principal, car il est celui qui reçoit les signaux, il est le processus qui a threads.
Faire comme vous montrer, vous exécutez une application essentiellement « normale », sans caractéristiques de fil, que vous commencez à 1 fil et attendez jusqu'à ce qu'il se termine pour continuer.
Autres conseils
Si vous voulez avoir thread principal pour recevoir le CTRL + C signal tout en se joindre, il peut être fait en ajoutant délai d'appel join()
.
Ce qui suit semble fonctionner (ne pas oublier d'ajouter daemon=True
si vous voulez mettre fin principale en fait):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
En Python, il est vrai que les exceptions de KeyboardInterrupt
sont élevés que dans le thread principal de chaque processus. Mais comme d'autres réponses mentionnées à titre, il est vrai aussi que la méthode bloque Thread.join
le thread appelant, dont KeyboardInterrupt
exceptions . Voilà pourquoi Ctrl + C semble avoir aucun effet:. L'exécution dans les principaux restes de fil bloqués à la ligne thread.join()
Ainsi, une solution simple à votre question est d'abord, ajouter un argument timeout pour thread.join()
et mettre cet appel dans une boucle qui se termine lorsque les sorties de fil de l'enfant, de sorte que les exceptions de KeyboardInterrupt
peuvent être soulevées après chaque délai d'attente, et d'autre part, faire le fil de l'enfant daemonic , ce qui signifie que son parent (le fil conducteur ici) va le tuer quand il sort (uniquement fils non démon ne sont pas tués, mais rejoint lorsque leurs sorties mères):
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
Mais une meilleure solution, si vous contrôlez le code du thread enfant, est d'informer le fil de l'enfant à la sortie gracieusement (au lieu de brusquement comme avec la première solution), par exemple avec 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