BackgroundTasks 如何巧妙驾驭多任务并发?

一、BackgroundTasks 工作原理与使用场景

graph TD A[用户请求] --> B[路由处理函数] B --> C{是否包含BackgroundTasks} C -- 是 --> D[将任务加入队列] C -- 否 --> E[直接返回响应] D --> F[返回响应] F --> G[后台执行队列任务] G --> H[任务执行完成]

二、多任务并发控制策略

2.1 异步任务队列实现

python 复制代码
from fastapi import BackgroundTasks, FastAPI
from pydantic import BaseModel
import asyncio

app = FastAPI()

class TaskRequest(BaseModel):
    data: str
    priority: int = 1

async def process_task(task: TaskRequest):
    # 模拟耗时操作
    await asyncio.sleep(2)
    print(f"Processed: {task.data}")

@app.post("/submit-task")
async def submit_task(
    request: TaskRequest,
    background_tasks: BackgroundTasks
):
    semaphore = asyncio.Semaphore(5)  # 最大并发数控制
    background_tasks.add_task(
        run_with_concurrency_control,
        semaphore,
        process_task,
        request
    )
    return {"status": "Task queued"}

async def run_with_concurrency_control(semaphore, func, *args):
    async with semaphore:
        return await func(*args)

所需依赖:

ini 复制代码
fastapi==0.68.0
pydantic==1.10.7
uvicorn==0.15.0

2.2 优先级任务调度

通过装饰器实现优先级队列:

python 复制代码
from collections import deque

class PriorityQueue:
    def __init__(self):
        self.high = deque()
        self.normal = deque()

    def add_task(self, task, priority=1):
        if priority > 1:
            self.high.append(task)
        else:
            self.normal.append(task)

    def get_next(self):
        return self.high.popleft() if self.high else self.normal.popleft()

三、课后 Quiz

问题 1

当需要处理耗时 10 分钟以上的任务时,应该选择 BackgroundTasks 还是 Celery?

答案解析: BackgroundTasks 适合短时任务(<5分钟),长时间任务建议使用 Celery:

  1. BackgroundTasks 依赖请求生命周期
  2. 进程重启会导致任务丢失
  3. 缺乏分布式任务追踪能力

问题 2

以下哪种方式可以有效防止并发任务数超过系统负载? A) 使用线程池 B) 设置 Semaphore C) 增加服务器数量 D) 使用数据库锁

正确答案:B

解析:Semaphore 是控制并发的原生机制,可在应用层直接限制并行任务数

四、常见报错处理

4.1 422 Validation Error

现象

json 复制代码
{
    "detail": [
        {
            "loc": ["body", "priority"],
            "msg": "field required",
            "type": "value_error.missing"
        }
    ]
}

解决方案

  1. 检查 Pydantic 模型定义是否缺少 required 字段
  2. 使用 Optional 类型标记可选参数:
python 复制代码
from typing import Optional

class TaskRequest(BaseModel):
    data: str
    priority: Optional[int] = 1  # 默认值设为1

4.2 后台任务未执行

可能原因

  1. 未正确传递 background_tasks 参数
  2. 任务函数未使用 async 定义
  3. 请求提前中断导致任务队列未执行

排查步骤

python 复制代码
@app.post("/debug-task")
async def debug_task(
    background_tasks: BackgroundTasks
):
    def sync_task():
        print("Debug task executed")
    
    background_tasks.add_task(sync_task)
    return {"status": "Debug task queued"}

检查控制台输出确认任务执行情况

4.3 并发超限错误

现象 :RuntimeError: Too many concurrent tasks 预防措施

  1. 在应用启动时初始化全局信号量
  2. 结合队列系统实现流量削峰
python 复制代码
@app.on_event("startup")
async def init_concurrency_control():
    app.state.task_semaphore = asyncio.Semaphore(10)
相关推荐
涡能增压发动积2 分钟前
Browser-Use Agent使用初体验
人工智能·后端·python
探索java24 分钟前
Spring lookup-method实现原理深度解析
java·后端·spring
lxsy31 分钟前
spring-ai-alibaba 之 graph 槽点
java·后端·spring·吐槽·ai-alibaba
码事漫谈41 分钟前
深入解析线程同步中WaitForSingleObject的超时问题
后端
兵临天下api43 分钟前
【干货满满】如何处理requests库调用API接口时的异常情况
trae
qianmoQ1 小时前
GitHub 趋势日报 (2025年08月01日)
github
码事漫谈1 小时前
C++多线程同步:深入理解互斥量与事件机制
后端
少女孤岛鹿1 小时前
微服务注册中心详解:Eureka vs Nacos,原理与实践 | 一站式掌握服务注册、发现与负载均衡
后端
CodeSaku1 小时前
是设计模式,我们有救了!!!(四、原型模式)
后端
Ray661 小时前
「阅读笔记」零拷贝
后端