Python开发:BackgroundTasks和asyncio.create_task()的区别
在前一篇文章中https://blog.csdn.net/zhang_jiamin/article/details/153329238?spm=1011.2415.3001.5331介绍了接口的场景设计,采用的设计方案是:后端异步任务 + 轮询进度接口,让前端立即得到“任务已开始”的响应,然后再异步处理解析任务。
其中涉及到了后端异步任务,有两个常用方法:
BackgroundTasks和asyncio.create_task()
先说主要区别:
特性 | BackgroundTasks(FastAPI内置) | asyncio.create_task()(原生异步) |
---|---|---|
触发时机 | 在响应返回之后由 FastAPI 调用 | 在调用时立即启动后台任务 |
作用范围 | 仅限当前请求上下文(response 完成后运行) | 独立运行,不依赖请求上下文 |
适合的场景 | 轻量、短时间任务(如发通知、写日志) | 长耗时任务(下载、解析、训练) |
生命周期 | 绑定请求生命周期,随请求结束 | 独立任务,可持续运行几分钟甚至几小时 |
错误处理 | 异常不会被捕获到主协程 | 可自行捕获、记录日志 |
状态追踪 | 不方便管理(一次性) | 可以保存在全局 dict / Redis 中,支持查询进度 |
我选择的是:asyncio.create_task()
原因:
我的场景是:后端下载和解析 .aab 文件,大约需要 1 分钟。
属于一个耗时任务,同时还希望:
- 前端立即返回 task_id
- 后端继续后台运行
- 前端可查询进度/结果
BackgroundTasks 更适合「小收尾工作」的场景(如发邮件、打日志)。
如果使用使用 BackgroundTasks:
from fastapi import BackgroundTasks@router.post("/app/aabParse")
async def parse_aab_api(url: str = Form(...), background_tasks: BackgroundTasks):background_tasks.add_task(download_and_parse, url)return {"message": "任务开始执行"}
会出现:
- FastAPI 会等响应返回后才启动任务(非实时)。
- 无法获取 task_id,也无法查询任务状态。
- 若进程重启,任务会丢失。
使用 asyncio.create_task()
task_id = str(uuid.uuid4())
asyncio.create_task(process_aab_task(task_id, url))
return {"task_id": task_id}
优点:
- 启动时立刻异步执行;
- 可保存 task_id;
- 可在 /status/{task_id} 接口轮询结果;
- 异常日志可控;
- 扩展性强(可后续接入 Celery 或 Redis)。
最终代码如下
@router.post("/app/aabParse")
async def parse_aab_api(url: str = Form(...)):logger.info(f'[parse_aab_api] url: {url}')task_id = str(uuid.uuid4())task = asyncio.create_task(process_aab_task(task_id, url))async with tasks_lock:tasks[task_id] = {"status": "processing", "result": None, "task": task}return JSONResponse(content={"code": 0,"message": "success","data": {"task_id": task_id,"task_message": "任务已开始,请稍后查询结果"}})