我在某处读到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 来杀死线程?
答案 0 :(得分:13)
如果您希望主线程在加入时接收 CTRL + C 信号,可以通过向join()
调用添加超时来完成。< / p>
以下似乎有效(如果您希望main实际结束,请不要忘记添加daemon=True
):
thread1.start()
while True:
thread1.join(600)
if not thread1.isAlive():
break
答案 1 :(得分:10)
问题在于您使用的是thread1.join()
,这将导致程序等到该线程完成继续。
信号将始终被主进程捕获,因为它是接收信号的信号,它是具有线程的进程。
如你所示,你基本上运行一个没有线程功能的'普通'应用程序,因为你启动1个线程并等到它完成继续。
答案 2 :(得分:2)
在Python中,确实KeyboardInterrupt
异常仅在每个进程的主线程中引发。但是正如提到的其他答案一样,方法Thread.join
阻止了调用线程,包括包括 KeyboardInterrupt
异常,这也是事实。这就是为什么 Ctrl + C 似乎无效的原因:主线程中的执行在thread.join()
行处仍然被阻止。
因此,对您的问题的一个简单解决方案是向Thread.join
添加一个超时参数,并将其放入主线程的无限循环中,以便可以在每次超时后引发KeyboardInterrup
异常,并使子线程daemonic,这意味着它的父线程(这里的主线程)在退出时将杀死它(只有非守护进程线程不会被杀死,而是在其父退出时加入):
def main():
try:
thread = threading.Thread(target=f, daemon=True) # create a daemon child thread
thread.start()
while True:
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