Uvicorn自动重载时无法终止子线程问题求助
解决Uvicorn重载时后台线程残留的问题
你的问题核心是:启动的后台线程默认是非守护线程,且没有优雅停止的逻辑,导致Uvicorn重载时旧进程无法正常退出,线程持续运行。下面是具体的解决思路:
1. 标记线程为守护线程
默认threading.Thread的daemon参数为False,非守护线程会强制阻止主进程退出。创建线程时显式设置daemon=True,主进程结束时系统会自动终止这类线程:
import threading import time def update_loop(): while True: print("my print from my thread") time.sleep(5) # 启动守护线程 threading.Thread(target=update_loop, daemon=True).start()
⚠️ 注意:如果线程内有需要收尾的操作(比如写入文件、关闭数据库连接),这个方法会直接终止线程,可能导致数据丢失,建议结合下面的优雅停止方案。
2. 给线程添加优雅停止信号
用threading.Event作为线程的停止标志,在线程循环中检测该标志,收到停止信号后安全退出。同时利用FastAPI的shutdown事件触发停止逻辑:
from fastapi import FastAPI import threading import time app = FastAPI() # 线程安全的停止信号 stop_event = threading.Event() def update_loop(): while not stop_event.is_set(): print("my print from my thread") time.sleep(5) # 这里添加收尾操作,比如保存数据、释放资源 print("后台线程已优雅退出") # 应用启动时启动线程 @app.on_event("startup") def start_background_task(): threading.Thread(target=update_loop).start() # 应用关闭时触发停止信号 @app.on_event("shutdown") def stop_background_task(): stop_event.set()
这样Uvicorn触发shutdown流程时,会通知线程停止,线程完成当前循环后安全退出,不会残留。
3. 不要在模块级别启动线程
如果你的线程是直接写在api.py的模块最外层(比如导入模块就启动),Uvicorn重载时会重新导入模块,但旧进程的线程仍在运行。必须把线程启动逻辑放在FastAPI的startup事件中,确保只有应用实例启动时才创建线程,且关闭时能通过shutdown事件清理。
4. 生产环境建议
开发环境用--reload没问题,但生产环境不要依赖这个机制。生产环境可以用Uvicorn的多进程模式,或者用专门的任务调度工具管理后台定时任务,避免手动创建线程带来的进程管理问题。
内容的提问来源于stack exchange,提问作者Jules Civel
相关产品推荐
相关产品推荐

