如何用Ctrl+C结束子线程?
我想在Python中通过按下 Ctrl+C 来停止一个进程的执行。但是我看到有说法提到,KeyboardInterrupt
这个异常只会在主线程中被触发。我还了解到,当子线程在执行时,主线程是被阻塞的。那么我该怎么结束子线程呢?
比如,下面的代码按下 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
3 个回答
8
KeyboardInterrupt
异常只会在每个进程的主线程中被触发。但是,Thread.join
方法会阻塞调用它的线程,这包括 KeyboardInterrupt
异常。这就是为什么按 Ctrl+C 似乎没有任何效果的原因。
解决这个问题的一个简单方法是让 Thread.join
方法设置超时,这样就可以解除对 KeyboardInterrupt
异常的阻塞,并将子线程设置为 守护线程,这样父线程在退出时可以杀掉它(非守护线程在退出时不会被杀掉,而是由父线程等待它结束):
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
如果你能控制子线程的代码,另一个更好的解决方案是通知子线程优雅地退出(而不是像简单解决方案那样突然退出),比如使用 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
14
如果你想让主线程在等待其他线程结束的时候能够接收到 CTRL+C 的信号,可以通过给 join()
方法加上超时来实现。
下面的代码看起来是有效的(如果你希望主线程真的能结束,别忘了加上 daemon=True
):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
11
这里的问题是你在使用 thread1.join()
,这会导致你的程序在这个线程完成之前一直等待,不能继续执行。
信号总是会被主进程接收到,因为信号是由主进程接收的,主进程才有线程。
按照你展示的方式,你实际上是在运行一个“普通”的应用程序,没有使用线程的特性,因为你只启动了一个线程,并且在它完成之前程序就停在那里不动。