Python异步编程async/await与FastAPI 核心知识笔记

Python 异步编程async/await与 FastAPI 核心知识笔记

1. async/await 核心概念

1.1 核心作用

  • async def:定义一个协程函数。调用时不会立即执行,而是返回一个协程对象,需要由事件循环驱动。
  • await挂起当前协程,将控制权交还给事件循环,直到等待的异步操作完成。它是实现“让出CPU”的关键。

1.2 关键认知纠正

常见误解 正确理解
async 代表“耗时操作” async 代表函数内有 “可等待的 I/O 操作”,适合网络请求、数据库查询等。耗时计算(CPU密集型)应使用普通 def
async 函数会自动并发 只有遇到 await 时才会让出控制权,否则会同步执行。
调用 async 函数必须加 await 不加 await 不会报错,但函数体不会执行,只返回一个协程对象。

1.3 await 的使用场景

  • async def 内部调用另一个异步操作(必须)。
  • 并发执行多个 I/O 任务(如 asyncio.gather)。
  • 等待异步库的 I/O 操作(如 aiohttpasyncpg)。

2. FastAPI 中的并发模型与接口定义

2.1 FastAPI 的底层架构

  • Worker 进程:通过 --workers 可启动多个进程,利用多核CPU。
  • 事件循环每个 Worker 进程内部有一个单线程的事件循环,负责调度所有 async 接口。

2.2 接口阻塞的原因

“一个接口慢,其他接口排队” 是因为事件循环被阻塞。常见原因:

  1. async def 接口中调用了同步阻塞代码(如 time.sleeprequests.get)。
  2. async def 接口中执行了CPU密集型计算。

2.3 如何选择接口定义方式(async def vs def

接口内部逻辑 推荐定义方式 原因
I/O 密集型,且使用支持 await 的异步库 async def 遇到 await 时让出事件循环,提升并发。
只有简单返回,或仅做参数校验 def 避免创建协程的开销,性能更优。
调用同步阻塞库(如 requestspsycopg2 def FastAPI 会自动放到线程池,不阻塞主事件循环。
CPU 密集型计算(图像处理、模型推理) def + run_in_threadpool 将任务交给线程池,避免阻塞。

2.4 核心铁律

如果代码里没有 await,就别用 async def
异步不是“银弹”,要为了“有 await 可写”而用 async

3. 数据库查询与 async 的选择

3.1 决策依据

是否使用 async 取决于数据库驱动是否支持异步,与查询速度无关。

驱动类型 推荐接口定义 示例库
异步驱动 async def + await asyncpg, aiomysql, asyncmy, SQLAlchemy 异步支持
同步驱动 def psycopg2, mysql-connector-python

3.2 错误示例

python

@app.get("/bad")
async def bad():               # ❌ 用了 async
    conn = psycopg2.connect()  # ❌ 同步驱动
    cur = conn.cursor()
    cur.execute("SELECT...")   # ⚠️ 会阻塞事件循环!
    return cur.fetchall()

3.3 正确示例

python

# ✅ 异步驱动
@app.get("/users")
async def get_users():
    async with async_session() as session:
        result = await session.execute(select(User))
        return result.scalars().all()

python

# ✅ 同步驱动
@app.get("/users")
def get_users():
    conn = psycopg2.connect(...)
    cur = conn.cursor()
    cur.execute("SELECT * FROM users")
    return cur.fetchall()

4. Python 异步 vs Java 多线程

维度 Python (asyncio) Java (传统多线程) Java (虚拟线程)
底层模型 单线程 + 事件循环 N 个 OS 线程(重量级) M 个虚拟线程挂载于 N 个 OS 线程
CPU 密集型 差(只用1核) 优秀(用满多核) 优秀(用满多核)
I/O 密集型(高并发) 优秀(切换开销极小) 差(线程多时切换开销大) 极优秀(极轻量,自动调度)
编程复杂度 高(async/await 传染) 中等(用线程池管理) 低(写同步代码即可)

4.1 核心结论

  • “多”不一定好:对于 I/O 密集型任务,Python 的单线程+事件循环模型往往比 Java 旧版重量级线程池更高效。
  • “并行”不等同于“并发”async 解决的是并发(同时处理多个连接),而非并行(同时执行多个计算)。

5. 关键记忆点

  1. async def:告诉 Python,“我这个函数里面可能有 await,允许我在等待时被切换出去。”
  2. await:告诉事件循环,“我现在要去等一个结果(网络/数据库),你先去忙别的,有结果了叫我。”
  3. 异步不是让代码跑得更快,而是让服务在等待 I/O 时能处理更多请求
  4. async 是“并发”的语法糖,目的是在 I/O 等待时最大化 CPU 利用率
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容