Python Asyncio Quart问题:任务已销毁但仍处于待处理状态
解决Quart + Asyncio的"Task was destroyed but it is pending!"异常
问题详情
遇到Asyncio与Quart结合时的异常,错误核心提示:
Task was destroyed but it is pending!
task: <Task pending name='Task-1672902' coro=<ASGIHTTPConnection.handle_messages() running at /usr/local/lib/python3.8/site-packages/quart/asgi.py:56> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7feb66ff0190>()]>
异常指向Quart的ASGIHTTPConnection.handle_messages()函数,当前环境:
- Python版本:3.8.12
- Quart版本:0.19.3
无法稳定复现该问题,缺少更多上下文追踪信息。
可能的原因
- 客户端提前断开连接:用户在请求处理过程中主动关闭浏览器或客户端连接,导致Quart负责处理该连接的异步任务还处于pending状态就被销毁
- 后台任务管理不当:应用中存在未被正确await或未在 shutdown 阶段处理的后台任务,进程退出时被强制销毁
- 版本兼容性bug:Quart 0.19.3与Python 3.8的异步任务调度机制存在已知兼容问题,后续版本已修复部分ASGI连接处理逻辑
可行的解决方案
升级Quart到最新稳定版
Quart 0.19.x属于较旧版本,后续发布的版本(如0.20.x及以上)修复了大量异步任务管理和ASGI相关的bug,执行命令升级:pip install --upgrade quart优雅处理连接断开异常
在请求处理的业务代码中捕获asyncio.CancelledError,确保任务能正常收尾,示例:from quart import Quart import asyncio app = Quart(__name__) @app.route("/") async def index(): try: # 你的业务逻辑 await asyncio.sleep(5) return "OK" except asyncio.CancelledError: # 连接断开时的收尾操作,比如释放资源 print("Connection cancelled, cleaning up") raise规范后台任务管理
如果使用自定义后台任务,需确保任务能被正确跟踪和处理:- 使用
asyncio.create_task()创建任务后,保存任务引用 - 在应用 shutdown 时(通过
@app.before_serving或@app.after_serving钩子)主动取消任务并等待其结束:background_tasks = set() @app.route("/start-task") async def start_task(): task = asyncio.create_task(long_running_task()) background_tasks.add(task) task.add_done_callback(background_tasks.discard) return "Task started" async def long_running_task(): try: await asyncio.sleep(30) except asyncio.CancelledError: pass @app.before_serving async def setup(): app.background_tasks = set() @app.after_serving async def cleanup(): for task in app.background_tasks: task.cancel() try: await task except asyncio.CancelledError: pass
- 使用
增加日志排查
添加异步任务的生命周期日志,方便后续复现时追踪任务上下文:import logging import asyncio logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def log_task(task): try: result = task.result() logger.info(f"Task {task.get_name()} completed with result: {result}") except asyncio.CancelledError: logger.info(f"Task {task.get_name()} was cancelled") except Exception as e: logger.error(f"Task {task.get_name()} failed: {e}", exc_info=True) # 创建任务时添加日志回调 task = asyncio.create_task(handle_messages()) task.add_done_callback(log_task)
内容的提问来源于stack exchange,提问作者Saad elkheety
相关产品推荐
相关产品推荐

