Reputation: 1083
I would like to stop the execution of a process with Ctrl+C in Python. But I have read somewhere that KeyboardInterrupt
exceptions are only raised in the main thread. I have also read that the main thread is blocked while the child thread executes. So how can I kill the child thread?
For instance, Ctrl+C has no effect with the following code:
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
Upvotes: 24
Views: 43896
Reputation: 8097
KeyboardInterrupt
exceptions are raised only in the main thread of each process. But the method Thread.join
blocks the calling thread, including KeyboardInterrupt
exceptions. That is why Ctrl+C seems to have no effect.
A simple solution to your problem is to make the method Thread.join
time out to unblock KeyboardInterrupt
exceptions, and make the child thread daemonic to let the parent thread kill it at exit (non-daemonic child threads are not killed but joined by their parent at exit):
def main():
try:
thread = threading.Thread(target=f)
thread.daemon = True # let the parent kill the child thread at exit
thread.start()
while thread.is_alive():
thread.join(1) # time out not to block KeyboardInterrupt
except KeyboardInterrupt:
print "Ctrl+C pressed..."
sys.exit(1)
def f():
while True:
pass # do the actual work
A better solution if you control the code of the child thread is to notify the child thread to exit gracefully (instead of abruptly like with the simple solution), for instance using a threading.Event
:
def main():
try:
event = threading.Event()
thread = threading.Thread(target=f, args=(event,))
thread.start()
event.wait() # wait without blocking KeyboardInterrupt
except KeyboardInterrupt:
print "Ctrl+C pressed..."
event.set() # notify the child thread to exit
sys.exit(1)
def f(event):
while not event.is_set():
pass # do the actual work
Upvotes: 9
Reputation: 249
If you want to have main thread to receive the CTRL+C signal while joining, it can be done by adding timeout to join()
call.
The following seems to be working (don't forget to add daemon=True
if you want main to actually end):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
Upvotes: 14
Reputation: 851
The problem there is that you are using thread1.join()
, which will cause your program to wait until that thread finishes to continue.
The signals will always be caught by the main process, because it's the one that receives the signals, it's the process that has threads.
Doing it as you show, you are basically running a 'normal' application, without thread features, as you start 1 thread and wait until it finishes to continue.
Upvotes: 11