应显式捕获 KeyboardInterrupt 而非空 except,将其置于主循环外层 try 中,配合清理逻辑与 sys.exit(0);注意 atexit、子进程、临时文件、日志 handler 及多线程退出协调。

捕获 KeyboardInterrupt 时为什么不能只用空 except?
直接写 except: 会吞掉所有异常,包括 SystemExit、MemoryError 甚至你手动抛的 ValueError,导致调试困难或资源泄漏。真正需要拦截的只有用户按 Ctrl+C 触发的 KeyboardInterrupt。
- 必须显式写
except KeyboardInterrupt:,不要省略异常类型 - 如果同时要处理其他业务异常(如网络超时),应分开展开,避免混写
- 空
except KeyboardInterrupt:(即后面没任何语句)会让程序静默退出,但不会执行清理逻辑,等同于没处理
try/except 套在什么位置才真正有效?
放在主循环最外层或长期运行的阻塞调用外侧。比如你在 while True: 里做轮询,KeyboardInterrupt 只有在循环体内部被触发时才能被捕获;如果卡在某个没中断响应的 C 扩展调用里(如某些旧版 time.sleep() 在 Windows 上),可能延迟响应。
- 推荐结构:
try:→ 主逻辑(含循环)→except KeyboardInterrupt:→ 清理代码 →sys.exit(0) - 避免把
try套在单行语句上,例如try: input() except KeyboardInterrupt: ...,这样只能捕获一次输入中断,无法覆盖整个程序生命周期 - 若使用
asyncio,需配合signal模块监听SIGINT,因为KeyboardInterrupt不会自动抛进协程调度器
清理资源时哪些操作容易被忽略?
关闭文件、释放锁、断开数据库连接这些显性资源大家通常记得,但以下几类常被跳过:
-
atexit.register()注册的函数在KeyboardInterrupt下**不会自动触发**,必须手动调用或改用try/finally - 子进程(
subprocess.Popen)若未设置preexec_fn=os.setsid,直接kill()可能残留僵尸进程 - 临时文件(
tempfile.NamedTemporaryFile)若未显式.close()或用with,可能在中断时来不及自动清理 - 日志 handler(如
FileHandler)需调用handler.close(),否则最后一段日志丢失
多线程环境下怎么安全退出?
KeyboardInterrupt 默认只在主线程抛出,子线程不会自动收到。如果你开了后台线程(比如心跳上报、日志轮转),它们可能继续运行,导致进程无法真正退出。
立即学习“Python免费学习笔记(深入)”;
- 主线程捕获
KeyboardInterrupt后,应设置退出标志(如threading.Event),并调用join(timeout=3)等待子线程自然结束 - 避免在子线程里捕获
KeyboardInterrupt—— 它根本收不到 - 对无法协作退出的线程(如死循环中无检查点),可考虑设为
daemon=True,但要注意:daemon 线程退出时不执行finally和__del__,仍需确保关键资源已在主线程清理
SIGINT,或者用 signal.signal(signal.SIGINT, signal.SIG_IGN) 主动忽略。这种情况下,KeyboardInterrupt 根本不会抛出,得去查对应库的文档或源码。


















