FastAPI与Python中async/await调用是否会进入线程池?
FastAPI中async/await与线程池的关系及和Kotlin协程的对比
核心结论
- 纯异步IO的
async路由:不会进入线程池,和你熟悉的Kotlin协程异步IO场景逻辑一致,由事件循环(类似状态机)调度协程的挂起与恢复。 - 带阻塞IO的
async路由:FastAPI不会自动将阻塞操作丢进线程池,若不手动处理会卡死事件循环;你可以用asyncio.to_thread()或线程池包装阻塞代码,此时任务才会进入线程池执行。 - FastAPI的同步路由(不带
async):默认会在线程池里运行,这也是最佳实践文档建议“别给只有阻塞IO的路由加async”的原因——同步路由的线程池管理是自动的,比手动处理async里的阻塞IO更省心。
和Kotlin协程的差异与共性
共性
两者本质都是基于状态机的协程实现,在纯异步IO场景下,都是通过挂起等待IO完成来切换任务,不需要依赖线程池:
- Kotlin里用挂起函数做异步IO时,协程会在状态机的调度下挂起,IO完成后恢复执行,无需额外线程。
- Python asyncio的事件循环也是单线程内调度协程,遇到IO挂起时就切换到其他就绪协程,等IO回调触发再继续当前协程。
差异
- 线程调度灵活性:Kotlin协程支持多线程调度器(比如
Dispatchers.Default是线程池,Dispatchers.Main是单线程),可以更灵活地在不同线程间切换协程;而Python asyncio默认是单线程事件循环,跨线程需要手动用线程池或进程池配合。 - 阻塞操作处理:Kotlin协程里如果调用阻塞代码,你需要显式指定
Dispatchers.IO(线程池)来避免阻塞主线程;Python asyncio里则需要用asyncio.to_thread()把阻塞代码丢到线程池,否则会阻塞整个事件循环。
代码示例
纯异步IO(不进线程池)
from fastapi import FastAPI import aiohttp app = FastAPI() @app.get("/async-io") async def async_io_example(): async with aiohttp.ClientSession() as session: async with session.get("https://example.com") as resp: return await resp.text()
这个路由里的aiohttp调用是纯异步IO,全程由asyncio事件循环调度,不会用到线程池。
阻塞IO手动包装(进线程池)
from fastapi import FastAPI import asyncio import requests app = FastAPI() @app.get("/blocking-in-async") async def blocking_in_async(): # 用to_thread把阻塞的requests调用丢进线程池 result = await asyncio.to_thread(requests.get, "https://example.com") return result.text
这里的requests.get是阻塞IO,通过asyncio.to_thread()放到线程池执行,不会阻塞事件循环。
内容的提问来源于stack exchange,提问作者Gleichmut
相关产品推荐
相关产品推荐

