Python多线程中捕获KeyboardInterrupt后仍出现回溯的原因
问题:为何捕获KeyboardInterrupt后仍出现线程模块的回溯信息?
我正在尝试理解Python中异常抛出的工作机制,编写了如下代码:
import threading import time def first_thread(): print(f'Start first thread') time.sleep(2) print(f'End first thread') def second_thread(): print(f'Start second thread') time.sleep(5) print(f'End second thread') if __name__ == '__main__': first = threading.Thread(target=first_thread) second = threading.Thread(target=second_thread) try: print(f'Current number of threads: {threading.active_count()}') first.start() second.start() print(f'Current number of threads: {threading.active_count()}') first.join() second.join() print(f'Current number of threads: {threading.active_count()}') except KeyboardInterrupt as exc: print(f'Get the following exception: {exc}')
运行该代码后的输出为:
test_test % python3 test_simple.py Current number of threads: 1 Start first thread Start second thread Current number of threads: 3 ^CGet the following exception: ^CException ignored in: <module 'threading' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'> Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1307, in _shutdown lock.acquire() KeyboardInterrupt
我的问题是:为何触发KeyboardInterrupt异常时,try-except语句已捕获该异常,但控制台仍会出现回溯信息?
回答
出现这个问题的核心原因是:你的try-except只捕获了主线程中join()调用时触发的KeyboardInterrupt,但Python解释器退出前的线程清理阶段,又在内部的_shutdown函数里触发了一次未被捕获的KeyboardInterrupt。
具体过程如下:
- 按下
Ctrl+C时,信号会发送给整个进程,所有线程都会收到KeyboardInterrupt。主线程在执行first.join()或second.join()时触发异常,被你的try-except捕获,打印提示后主线程继续执行到脚本末尾,进入退出流程。 - 解释器退出时,会调用
threading模块的_shutdown函数,该函数会尝试获取线程内部的锁来清理残留的线程资源。此时后台线程可能还处于sleep状态,再次触发KeyboardInterrupt,导致lock.acquire()调用抛出异常。 - 这个异常发生在解释器的清理阶段,不在你的
try-except代码块覆盖范围内,因此会被解释器当作未捕获异常打印回溯信息。
要解决这个问题,有两种常见方式:
- 设置子线程为守护线程:创建线程时添加
daemon=True参数,这样主线程退出时子线程会被强制终止,避免解释器进入复杂的清理流程:first = threading.Thread(target=first_thread, daemon=True) second = threading.Thread(target=second_thread, daemon=True) - 主动控制子线程退出:可以通过设置全局标志位,让子线程在循环中检查标志位主动退出,结合
join()等待子线程结束,避免残留线程触发清理异常。
内容的提问来源于stack exchange,提问作者Daniil Stepanov
相关产品推荐
相关产品推荐

