Ctrl-C ie KeyboardInterrupt 在 Python 中杀死线程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4136632/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ctrl-C i.e. KeyboardInterrupt to kill threads in Python
提问by Amit S
I read somewhere that KeyboardInterruptexception 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+Ccan never reach to the child thread. I tried the following code:
我在某处读到KeyboardInterrupt异常仅在 Python 的主线程中引发。我还读到主线程在子线程执行时被阻塞。那么,这是否意味着CTRL+C永远无法到达子线程。我尝试了以下代码:
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+Con 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?
在这种情况下,CTRL+C对执行没有影响。就好像听不到信号一样。我是否以错误的方式理解这一点?有没有其他方法可以使用CTRL+杀死线程C?
采纳答案by webbi
The problem there is that you are using thread1.join(), which will cause your program to wait until that thread finishes to continue.
问题在于您正在使用thread1.join(),这将导致您的程序等待该线程完成才能继续。
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.
如您所见,您基本上是在运行一个“普通”应用程序,没有线程功能,因为您启动 1 个线程并等待它完成继续。
回答by korc
If you want to have main thread to receive the CTRL+Csignal while joining, it can be done by adding timeout to join()call.
如果你想让主线程在加入时接收CTRL+C信号,可以通过添加超时来完成join()调用。
The following seems to be working (don't forget to add daemon=Trueif you want main to actually end):
以下似乎有效(daemon=True如果您希望 main 实际结束,请不要忘记添加):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
回答by Maggyero
In Python, it is true that KeyboardInterruptexceptions are raised only in the main thread of each process. But as other answers mentionned, it is also true that the method Thread.joinblocks the calling thread, includingKeyboardInterruptexceptions. That is why Ctrl+Cseems to have no effect: the execution in the main thread remains blocked at the line thread.join().
在 Python 中,确实KeyboardInterrupt只有在每个进程的主线程中才会引发异常。但正如其他答案所提到的,该方法也确实会Thread.join阻塞调用线程,包括KeyboardInterrupt异常。这就是为什么Ctrl+C似乎没有效果的原因:主线程中的执行仍然在行处阻塞thread.join()。
So a simple solution to your question is to firstly, add a timeout argumentto thread.join()and put that call in a loop that ends when the child thread exits, so that KeyboardInterruptexceptions 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):
因此,一个简单的解决你的问题是首先,添加超时参数来thread.join(),并把该呼叫在一个循环结束时子线程退出,使KeyboardInterrupt异常可以在每个超时后提出的,其次,让孩子线程邪,这意味着它的父线程(这里的主线程)会在它退出时杀死它(只有非守护线程不会被杀死,而是在它们的父线程退出时加入):
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:
但是,如果您控制子线程的代码,则更好的解决方案是通知子线程优雅地退出(而不是像第一个解决方案那样突然退出),例如使用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

