为何FastAPI中同步函数表现如异步,异步函数反之?
FastAPI同步与异步函数的行为差异解析
场景回顾
你编写了一个带10秒time.sleep的同步FastAPI接口:
from fastapi import FastAPI import time import random import asyncio app = FastAPI() @app.get("/") def roots(req_id): print("Waiting for Sleep by", req_id) time.sleep(10) print("Sleep completed for ", req_id) return "Hello"
测试时发现:两次间隔3秒发起的请求,响应时间均为~10.2秒;但给函数加上async关键字后,两次请求的响应时间却出现明显差异。
为什么同步函数表现出“异步”的并发效果?
FastAPI依赖Uvicorn这类ASGI服务器,这类服务器会通过线程池处理同步接口请求:
- 当第一个请求进入,线程池分配线程A执行
time.sleep(10)——这是IO阻塞操作,线程A会被操作系统挂起,暂时让出CPU资源 - 3秒后第二个请求进入,线程池分配线程B处理,线程B同样执行
time.sleep(10) - 两个线程的sleep操作是并行的:第一个请求从发起到完成耗时10秒左右,第二个请求的sleep与第一个的sleep重叠了3秒,所以从发起算也是10秒左右,最终两者响应时间接近
注意:如果同步函数是CPU密集型操作(比如大量循环计算),线程会持续占用CPU,此时第二个请求必须等第一个完成才能开始,响应时间就会变成~13.2秒——IO阻塞和CPU密集型阻塞的处理逻辑完全不同。
为什么加async关键字后,响应时间反而出现差异?
给函数加async后,它变成了异步函数,FastAPI会用单线程事件循环处理,不再分配独立线程:
- 但你代码里的
time.sleep(10)是同步阻塞调用,它会直接卡住整个事件循环——事件循环在执行time.sleep时,完全无法处理其他请求 - 场景还原:
- 第一个请求进入,事件循环执行异步函数,遇到
time.sleep(10),事件循环被阻塞 - 3秒后第二个请求进入,只能排队等待
- 第一个请求的sleep完成后,事件循环才开始处理第二个请求,执行它的
time.sleep(10)
- 第一个请求进入,事件循环执行异步函数,遇到
- 最终第一个请求耗时10.2秒,第二个请求从发起算要等7秒(第一个sleep剩余时间)+10秒=17.2秒,两者响应时间差异明显
如果要让异步函数实现真正的并发,应该把time.sleep(10)替换为await asyncio.sleep(10)——这是异步阻塞操作,会主动让出事件循环,此时第二个请求可以在第一个sleep期间被处理,响应时间会回到和同步函数类似的状态。
内容的提问来源于stack exchange,提问作者sachin murali
相关产品推荐
相关产品推荐

