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)
相关推荐
yihuiComeOn24 分钟前
[源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅
java·后端·spring
散峰而望1 小时前
C++数组(二)(算法竞赛)
开发语言·c++·算法·github
e***71671 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
程序猿小蒜1 小时前
基于springboot的的学生干部管理系统开发与设计
java·前端·spring boot·后端·spring
银空飞羽1 小时前
让Trae CN SOLO自主发挥,看看能做出一个什么样的项目
前端·人工智能·trae
q***56382 小时前
Spring容器初始化扩展点:ApplicationContextInitializer
java·后端·spring
菜鸟‍2 小时前
【后端学习】MySQL数据库
数据库·后端·学习·mysql
Codebee3 小时前
30 分钟落地全栈交互:OneCode CLI+SVG 排课表实战
后端
TechTrek3 小时前
Spring Boot 4.0正式发布了
java·spring boot·后端·spring boot 4.0
飞梦工作室4 小时前
企业级 Spring Boot 邮件系统开发指南:从基础到高可用架构设计
java·spring boot·后端