守护线程在主线程退出时被强制终止,不执行finally、不触发异常处理、不调用资源关闭逻辑,仅适用于可随时中断的无状态后台任务;其终止由解释器统一回收,时机取决于最后一个非守护线程结束。

守护线程在主线程退出时被强制终止,不是“自然结束”,而是解释器层面的硬性回收——它不执行 finally、不触发异常处理器、不调用资源关闭逻辑,只适用于可随时中断、无状态的后台任务。
守护线程的终止时机由主线程决定
Python(或 JVM)只等待所有非守护线程完成,一旦最后一个非守护线程(通常是主线程)结束,整个程序立即进入退出流程。此时所有仍在运行的守护线程会被强制中止,不管它们处于什么状态:正在循环、阻塞在 sleep、还是写入日志中途。
- 主线程调用 sys.exit()、执行完 main 函数、或抛出未捕获异常,都算“退出”
- 子线程默认继承父线程 daemon 属性,主线程是非守护的,所以新 Thread 默认也是非守护
- 只要还有一个非守护线程活着,程序就不会退出,守护线程就能继续跑
setDaemon(True) 必须在 start() 前设置
daemon 是线程对象创建后就固定的属性,只能在 NEW 状态下修改。start() 之后再调用 setDaemon(True) 会直接抛 RuntimeError,因为线程已进入 RUNNABLE 状态,不可变更角色。
- 正确顺序:创建线程 → 设置 daemon=True(或调用 setDaemon(True))→ 调用 start()
- 推荐用构造参数:threading.Thread(target=task, daemon=True),更清晰且不易出错
- 不能在 run() 方法里动态切换,也没有运行时查询或重设机制
清理逻辑不会被执行,不要依赖 finally 或 shutdown hooks
守护线程被终止是静默的:没有 traceback、不进 except、跳过 finally、不触发 atexit 注册函数,甚至 __del__ 都不一定调用。它的退出不由代码控制,而是由解释器统一回收。
- 在 finally 里 print("cleanup done")?大概率看不到输出
- 用 threading.Timer 或 while 循环做周期性清理?必须接受某次循环可能被截断
- 涉及文件 close()、数据库 commit()、socket 断开等操作,绝不能放在守护线程里
适合做什么,不适合做什么
守护线程的价值在于“轻量后台服务”,核心是“丢了也不影响正确性”。它不是优雅退出机制,而是简化生命周期管理的取舍设计。
- 适合:心跳上报、指标采集、缓存预热、简单日志刷盘(非关键日志)
- 不适合:事务提交、文件写入完成确认、连接池优雅关闭、任何需原子性收尾的操作
- 若任务偶尔需要可靠执行,应改用非守护线程 + 显式 join() 或信号通知机制










