FastAPI同步处理器是否可能阻塞应用主线程?技术问询
示例代码
from fastapi import FastAPI import socket app = FastAPI() @app.get("/") async def root(): return {"message": "Hello World"} @app.get("/healthcheck") def health_check(): result = some_network_operation() return result def some_network_operation(): HOST = "192.168.30.12" # 该主机不存在,连接会超时 PORT = 4567 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(10) s.connect((HOST, PORT)) s.sendall(b"Are you ok?") data = s.recv(1024) print(data)
场景与问题
该应用包含两个路由:
/:异步处理器/healthcheck:同步处理器
调用/healthcheck时,因套接字连接超时需10秒完成,但此时调用/可立即返回响应——这是因为FastAPI会将同步处理器放在外部线程池中运行,不会阻塞主线程的事件循环。
问题:是否有可能通过在health_check方法中执行某些操作(比如获取全局解释器锁GIL,或其他类型的锁)来阻塞应用主线程?
解答
可以,但取决于具体操作类型:
全局锁资源争抢
如果在同步函数里获取一个主线程也会访问的全局锁(比如定义一个全局的threading.Lock()实例),并长时间持有不释放,当主线程(比如异步路由里的逻辑)尝试获取这个锁时,就会被阻塞。不过这是阻塞了主线程的业务逻辑,并非直接卡死事件循环。GIL的影响
Python的GIL是线程执行字节码时持有的锁,同步线程池中的线程如果执行计算密集型任务,会长时间持有GIL,但这只会影响其他CPU绑定的线程,不会阻塞异步主线程的事件循环——因为事件循环大多时间在等待IO操作,不会持续占用GIL。只有当主线程需要执行CPU密集型任务时,才会被GIL抢占CPU时间,但这不算阻塞主线程本身。直接操作事件循环
如果在同步函数里通过非常规手段操作事件循环(比如调用asyncio.get_event_loop().run_until_complete(),或者持有事件循环的内部锁),可能会直接卡死主线程的事件循环。但这种操作属于非业务场景的非常规用法,一般不会出现在正常代码中。
总结:常规锁操作不会直接阻塞FastAPI的主线程事件循环,但如果同步线程与主线程争抢同一全局锁资源,或是通过非常规手段操作事件循环,确实可能导致主线程的逻辑被阻塞。
内容的提问来源于stack exchange,提问作者Dacite

