我在某处读到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
在这种情况下,CTRL + C对执行没有影响。它就像是无法收听信号。我理解这是错误的方式吗?有没有其他方法可以使用CTRL + C杀死线程?
问题是你正在使用thread1.join()
,这将导致你的程序等到该线程完成继续。
信号将始终被主进程捕获,因为它是接收信号的信号,它是具有线程的进程。
如你所示,你基本上运行一个没有线程功能的“普通”应用程序,因为你启动1个线程并等到它完成继续。
如果你想让主线程在加入时接收CTRL + C信号,可以通过向join()
调用添加超时来完成。
以下似乎工作(如果你想要main实际结束,别忘了添加daemon=True
):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
在Python中,确实只在每个进程的主线程中引发了KeyboardInterrupt
异常。但正如其他答案所提到的那样,Thread.join
方法阻塞调用线程也是如此,包括KeyboardInterrupt
异常。这就是为什么Ctrl + C似乎没有效果:主线程中的执行仍然在thread.join()
行被阻止。
因此,对你的问题一个简单的解决方案是首先,向thread.join()
添加一个超时参数,并将该调用放在一个循环中,该循环在子线程退出时结束,这样每次超时后都可以引发KeyboardInterrupt
异常,其次,生成子线程daemonic,这意味着它的父(这里的主线程)会在它退出时杀死它(只有非守护程序线程没有被杀死但是当它们的父命令退出时才会被连接):
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
但是,如果你控制子线程的代码,更好的解决方案是通知子线程优雅地退出(而不是像第一个解决方案那样突然),例如使用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