news 2026/4/18 5:55:13

别被 `run_in_threadpool` 骗了,它只是个“背锅侠”!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别被 `run_in_threadpool` 骗了,它只是个“背锅侠”!

如果你在写 FastAPI 或者基于 Starlette 的应用,那你一定遇到过这种进退两难的时刻:
你手里有一段祖传的同步阻塞代码(比如老旧的requests.get或者某个不支持异步的数据库驱动),但你的路由是被async def定义的“纯血异步”函数。

这时候,如果你直接把同步代码塞进去,整个异步事件循环就会像被施了定身法一样,瞬间卡死,吞吐量直接清零。

于是,你开始疯狂查阅文档,终于找到了那个金光闪闪的 API——run_in_threadpool
你兴奋地把它包在同步代码外面,一跑,果然不卡了!你长舒一口气,以为自己完美解决了并发问题。

但真相是:你并没有解决阻塞,你只是花钱雇了个“背锅侠”。而且,如果不了解它的底细,这个背锅侠迟早会把你的服务器搞垮。

今天,我们就来扒一扒run_in_threadpool的底裤,看看这玩意儿到底是个啥。


第一幕:微波炉与傻站着的厨师

要彻底懂这其中的奥秘,我们先来复习一下“同步(Sync)”和“异步(Async)”到底有什么区别。

把我们的程序想象成一家高档餐厅,而服务器的 CPU 和主事件循环,就是这家餐厅里唯一的超级服务员

真正的异步(比如httpx.AsyncClient):微波炉模式
服务员接到客人的点单,把菜放进微波炉,按下 10 分钟倒计时。然后立刻转身去招呼其他几百个客人。微波炉“叮”一声响了(I/O 完成通知),服务员再去端菜。
结果:单核(一个服务员)轻松应对千万并发,没有任何人闲着。

同步阻塞(比如requests.get):傻站着模式
服务员接到点单,把菜下锅,然后死死盯着这口锅看 10 分钟。这 10 分钟内,门外排队的几百个客人全都在骂街,因为服务员被卡住了。
结果:吞吐量暴跌,服务器“假死”。


第二幕:run_in_threadpool登场,背锅侠就位!

为了防止超级服务员被这口锅卡死,FastAPI 祭出了run_in_threadpool这块创可贴。

当你使用它时,到底发生了什么?这段代码奇迹般地变成微波炉了吗?
绝对没有!这道菜依然需要人站在锅边死等!

run_in_threadpool只是做了一个障眼法:
超级服务员一看这道菜要死等,为了不让自己被骂,他立刻跑到后厨,花钱雇了一个临时工(开辟了一个子线程)

服务员对临时工说:“老哥,你帮我站在这口锅前死等 10 分钟,好了叫我哈!”
随后,超级服务员一身轻松,立刻转身回大堂继续接客了。

这,就是run_in_threadpool的真相:它并没有把同步变异步,它只是把“阻塞卡顿”的这口锅,甩给了后台新建的子线程。

对于主程序(超级服务员)来说,他不卡了;但对于这段代码本身,它依然是阻塞的。


第三幕:定时炸弹——“临时工”是有限的!

既然能雇临时工,那我把所有同步代码都用run_in_threadpool包起来,不就天下太平了?

如果你敢这么干,你的服务器离崩溃就不远了。因为这种“假异步”有一个致命的弱点:线程池的数量是有上限的。

在 FastAPI(底层依赖 anyio)中,默认的线程池大小通常是40 个
这意味着,你的后厨最多只能容纳 40 个临时工。

  • 假设你同时来了 40 个很慢的请求,超级服务员雇了 40 个临时工,大家都在后厨死等。
  • 当第41 个请求来的时候,灾难发生了。
  • 服务员回头一看:“卧槽,雇不到人了!” 此时,这第 41 个请求只能在大堂苦苦排队。
  • 从用户的角度来看,你的服务器依然卡死了,不管你怎么刷新都在转圈。

而如果是真正的异步(微波炉模式),哪怕同时来 10000 个请求,服务员只需要把 10000 盘菜放进 10000 个微波炉按一下按钮就行了,单线程就能搞定,根本不需要雇佣临时工。


第四幕:终极禁忌——千万别让临时工干“体力活”!

如果你觉得线程池耗尽已经够惨了,那run_in_threadpool还有一个能让 Python 直接吐血的禁忌:用它来执行 CPU 密集型任务(纯粹的数值计算、图像处理等)。

这又回到了 Python 的祖传大坑——GIL(全局解释器锁)

在同一间厨房(进程)里,有且只有一把“炒菜用的铁铲(GIL)”。

  • 如果是 I/O 阻塞(比如等网络请求):临时工是在“傻等”,不需要拿铁铲(会释放 GIL),超级服务员可以继续拿铁铲干活,两不相干。
  • 如果是重度计算:临时工必须死死握住这把铁铲疯狂干活。这时候,超级服务员如果想去大堂端盘子(执行 Python 异步逻辑),就会发现铁铲被临时工抢走了!两人开始疯狂争夺同一把铁铲。

最终的结果是:你的代码不仅依然只能在一个 CPU 核心上跑,而且因为主线程和子线程疯狂抢夺 GIL,整体性能反而比不加还要慢!


总结法则:拿捏 FastAPI 并发的正确姿势

写到这里,是时候总结一波避坑口诀了:

  1. 原汤化原食:在异步框架里,永远首选原生支持 async 的第三方库(如httpx,asyncpg,aiofiles)。这是真理,是王道。
  2. 创可贴用法:如果迫不得已必须调用同步 I/O 库,并且确定并发量不高,可以用run_in_threadpool(或者直接写普通的def路由,FastAPI 底层也是放进线程池)。它能救急,但别当饭吃。
  3. 计算任务靠边站:如果你有消耗大量 CPU 的任务(视频处理、大矩阵运算),请绝对远离run_in_threadpool!正确做法是开辟全新的厨房,使用ProcessPoolExecutor或外部任务队列(如 Celery + Redis),让它们在其他多核 CPU 上奔跑。

别再被run_in_threadpool骗了,它只是个尽职尽责的背锅侠。善待它,别把它累死。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 5:46:39

手把手教你用AI手势识别:上传图片秒出彩虹骨骼图,无需编程

手把手教你用AI手势识别:上传图片秒出彩虹骨骼图,无需编程 1. 快速了解AI手势识别技术 想象一下,你只需要上传一张手的照片,就能立即看到手指关节被彩色线条连接起来的炫酷效果。这就是我们今天要介绍的AI手势识别技术带来的神奇…

作者头像 李华
网站建设 2026/4/18 5:44:15

供应商评估模型:从课程设计、讲师背景、案例库到售后支持的全方位对比

选择培训或认证类供应商,本质上是在为企业的能力短板寻找最适配的“外挂大脑”。一个好的评估模型,应当把主观感受转化为可量化的指标。以下从课程设计、讲师背景、案例库、售后支持四个维度,提供一套加权评分框架。 一、评估模型核心逻辑 建议先确定各维度权重(总分100分…

作者头像 李华
网站建设 2026/4/18 5:44:15

STM32调试实战:Keil MDK + J-Link下局部变量消失的5种排查姿势

STM32调试实战:Keil MDK J-Link下局部变量消失的5种排查姿势 调试嵌入式系统时,局部变量突然"消失"是开发者常遇到的棘手问题。当你在Keil MDK环境中使用J-Link调试STM32,发现Watch窗口中的局部变量显示为"not in scope"…

作者头像 李华