KeyboardInterrupt是用户按下Ctrl+C时触发的继承自BaseException的异常,不能被except Exception捕获;必须显式用except KeyboardInterrupt处理,且需在except块中执行清理并调用sys.exit(0)确保优雅退出。

KeyboardInterrupt 是什么,为什么不能直接用 try-except 包裹主逻辑?
KeyboardInterrupt 是用户按下 Ctrl+C 时触发的异常,继承自 BaseException 而非 Exception。这意味着常规的 except Exception: 捕获不到它——很多初学者写成这样,结果 Ctrl+C 依然强制退出:
try:
while True:
time.sleep(1)
except Exception: # ❌ 不生效
print("退出中...")必须显式捕获 KeyboardInterrupt,且要注意:它可能在任意执行点抛出,包括 input()、time.sleep()、甚至某些 C 扩展调用中。
如何在循环中安全捕获并执行清理动作?
最常见场景是长运行循环(比如数据采集、服务轮询)。关键是在 except KeyboardInterrupt: 块里做资源释放,然后主动退出,避免残留状态:
- 用
finally或except KeyboardInterrupt:都可以,但finally会干扰正常退出逻辑(比如程序本应因其他异常终止),所以优先选明确捕获 - 清理动作(如关闭文件、断开数据库连接、重置 GPIO 引脚)必须放在
except块内,且建议加sys.exit(0)显式退出,防止后续代码意外执行 - 如果循环里有阻塞调用(如
input()),KeyboardInterrupt会立即触发;但若在纯计算密集型循环中,可能延迟响应——这时可定期插入time.sleep(0.01)让 Python 有机会检查信号
示例:
立即学习“Python免费学习笔记(深入)”;
import sys
import time
<p>try:
while True:
print("运行中...")
time.sleep(1)
except KeyboardInterrupt:
print("\n正在清理...")</p><h1>这里放你的清理逻辑,比如 close_db(), cleanup_gpio()</h1><pre class='brush:python;toolbar:false;'>sys.exit(0)</pre>多线程/多进程环境下 KeyboardInterrup 怎么传递和处理?
主线程收到 Ctrl+C 时,KeyboardInterrupt 默认只在主线程抛出,子线程不会自动中断。这会导致“看似退出,实则后台线程还在跑”的问题:
- 子线程里单独捕获
KeyboardInterrupt无效——它根本收不到这个信号 - 正确做法是:主线程捕获后,设置一个共享标志(如
threading.Event),各工作线程定期检查该标志并主动退出 - 对
multiprocessing,子进程默认不继承信号处理器,需手动用signal.signal(signal.SIGINT, signal.SIG_IGN)忽略信号,避免僵尸进程;主进程负责统一终止 - 使用
concurrent.futures时,executor.shutdown(wait=True)会等待任务结束,但不响应Ctrl+C——得配合signal模块提前取消 pending 任务
用 signal 模块替代 try-except 是否更可靠?
对于需要精细控制的场景(比如想让 Ctrl+C 触发特定回调,或屏蔽多次触发),signal 模块比 try-except 更底层也更灵活:
-
signal.signal(signal.SIGINT, handler)可以注册自定义函数,且该 handler 在任何时刻都可能被调用(包括在input()等阻塞调用期间) - 注意 handler 函数只能做轻量操作(如设 flag、记录日志),不能调用可能阻塞或不安全的函数(如
print()、sys.exit()),否则引发RuntimeError - 如果用了
signal,就别再依赖try-except KeyboardInterrupt,两者混用容易冲突;推荐单点控制——要么全用signal,要么全用except - Windows 下
SIGINT行为与 Unix 不同(例如无法在time.sleep()中中断),所以跨平台程序优先用try-except更稳妥
真正麻烦的是嵌套阻塞:比如程序既在 input() 又在 time.sleep(),还跑了线程。这时候光靠一层 except 不够,得结合 signal + 共享标志 + 主动超时检查。


















