在 FastAPI 的异步(async def)视图函数中直接调用同步方法(尤其是 I/O 密集型操作,如数据库查询、文件读写、网络请求等),会导致阻塞事件循环,严重降低并发性能,甚至使异步框架的优势完全丧失。
但现实开发中,我们常需调用第三方库(如 requests、pymysql、cv2 等)——它们大多是同步的。此时,不能直接调用,而应通过 asyncio.to_thread() 或 线程池 将其“异步化”。
✅ 正确做法:使用 asyncio.to_thread()(推荐,Python 3.9+)
import asyncio
from fastapi import FastAPI
app = FastAPI()
# 同步函数(模拟耗时I/O)
def sync_blocking_task(x: int) -> int:
import time
time.sleep(2) # 模拟阻塞
return x * 2
@app.get("/async-correct")
async def async_correct(x: int):
# ✅ 正确:将同步任务交给线程池执行,不阻塞事件循环
result = await asyncio.to_thread(sync_blocking_task, x)
return {"result": result}
✅ 优点:代码简洁,自动使用默认线程池,避免阻塞主事件循环。
🔁 兼容 Python < 3.9:手动创建线程池
import asyncio
from concurrent.futures import ThreadPoolExecutor
from fastapi import FastAPI
app = FastAPI()
# 全局线程池(可复用)
executor = ThreadPoolExecutor(max_workers=10)
def sync_task(x: int) -> int:
import time
time.sleep(2)
return x * 2
@app.get("/async-with-pool")
async def with_pool(x: int):
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(executor, sync_task, x)
return {"result": result}
⚠️ 注意:不要在每次请求中创建新线程池,应全局复用。
❌ 错误示范:直接调用同步函数
@app.get("/async-wrong")
async def async_wrong(x: int):
# ❌ 危险!会阻塞整个事件循环
result = sync_blocking_task(x) # 直接调用
return {"result": result}
后果:
- 所有并发请求都会被串行执行;
- 高并发下响应时间急剧上升;
- 失去使用 FastAPI 异步架构的意义。
🧠 何时可以安全调用同步代码?
仅限以下情况:
- 纯 CPU 计算且耗时极短(如简单数学运算、字符串处理);
- 不涉及 I/O、sleep、锁等待等阻塞操作。
例如:
@app.get("/safe-sync")
async def safe_sync(n: int):
# ✅ 安全:纯计算,无阻塞
total = sum(i * i for i in range(n))
return {"total": total}
💡 原则:只要函数可能“等待”(wait),就必须异步化或放线程池。
🛠 实战建议:数据库与 HTTP 请求
| 场景 | 推荐方案 |
|---|---|
| 数据库 | 使用异步驱动: - PostgreSQL → asyncpg + databases - MySQL → aiomysql / asyncmy - ORM → SQLModel(同步)或 Tortoise-ORM(异步) |
| HTTP 请求 | 用 httpx.AsyncClient 替代 requests |
| 文件读写 | 用 aiofiles 库 |
| 第三方同步库 | 包装进 asyncio.to_thread() |
示例:用 httpx 异步请求
import httpx
@app.get("/fetch")
async def fetch_data():
async with httpx.AsyncClient() as client:
resp = await client.get("https://api.example.com/data")
return resp.json()
✅ 总结
| 做法 | 是否推荐 | 说明 |
|---|---|---|
await asyncio.to_thread(sync_func) | ✅ 强烈推荐 | Python 3.9+ 最佳实践 |
loop.run_in_executor(thread_pool, sync_func) | ✅ 可用 | 兼容旧版本 |
在 async def 中直接调用 sync_func() | ❌ 严禁 | 阻塞事件循环,破坏并发 |
| 纯计算型同步代码 | ✅ 可接受 | 无 I/O,不影响性能 |
记住:FastAPI 的异步能力只有在全程非阻塞时才能发挥最大价值。遇到同步库,别犹豫——扔进线程池!