问题
I read somewhere that KeyboardInterrupt
exception is only raised in the main thread in Python. I also read that the main thread is blocked while the child thread executes. So, does this mean that CTRL+C can never reach to the child thread. I tried 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
In this case there is no effect of CTRL+C on the execution. It is like it is not able to listen to the signal. Am I understanding this the wrong way? Is there any other way to kill the thread using CTRL+C?
回答1:
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.
回答2:
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
回答3:
In Python, it is true that KeyboardInterrupt
exceptions are raised only in the main thread of each process. But as other answers mentionned, it is also true that the method Thread.join
blocks the calling thread, including KeyboardInterrupt
exceptions. That is why Ctrl+C seems to have no effect: the execution in the main thread remains blocked at the line thread.join()
.
So a simple solution to your question is to firstly, add a timeout argument to thread.join()
and put that call in a loop that ends when the child thread exits, so that KeyboardInterrupt
exceptions can be raised after each timeout, and secondly, make the child thread daemonic, which means that its parent (the main thread here) will kill it when it exits (only non-daemon threads are not killed but joined when their parent exits):
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
But a better solution, if you control the child thread's code, is to inform the child thread to exit gracefully (instead of abruptly like with the first solution), for instance with a 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
来源:https://stackoverflow.com/questions/4136632/ctrl-c-i-e-keyboardinterrupt-to-kill-threads-in-python