python async 启动协程时,如果当前协程不主动 await 释放,那就会一直占用事件循环,其他协程无法执行。
能想到的一些非常有风险的点:
- 程序员忘了写 await ,单纯靠 cr 很难完全发现,导致阻塞
- python 第三方库没有做 async 改造,不小心用了,导致阻塞
- 有可能本地运行时间短,到了线上数据量大了,阻塞时间变长
问了 ds 和 chatgpt , 看起来都没有很好的解决方案

python async 启动协程时,如果当前协程不主动 await 释放,那就会一直占用事件循环,其他协程无法执行。
能想到的一些非常有风险的点:
问了 ds 和 chatgpt , 看起来都没有很好的解决方案
1 pursuer Jun 25, 2025 第一条 python 的 await 如果写漏了,协程是不会运行的,第二条没改造的第三方库可以丢给其他线程跑,第三条和数据量大用不用协程都存在的一样的问题 |
2 simple2025 Jun 25, 2025 可以跑,阻塞就阻塞,你用 supervisor 多起几个进程就行了.虽然这种情况下有没有 async 都一样了. |
3 wudanyang OP @pursuer #1 第一条的意思是,程序员写了一个执行时间长的语句,但是中间没有使用 await 让出执行。 第二条确实可以放到线程执行,但 python 现在异步生态没这么好,很多都需要线程执行了 第三条如果直接用线程的话,线程是抢占式调度,不会让一个线程一直占着 cpu ,但是协程遇到数据量增长的情况,占用的 cpu 就会越来越多 |
4 wudanyang OP @chenqh #2 如果启动的是 web server ,同时来两个请求,耗时本来都很短。 这时候如果其中一个请求阻塞了,那另一个请求的耗时也跟着变长了。 |
5 yh7gdiaYW Jun 25, 2025 第二点是巨坑,光靠 review 很难 100%发现,只能结合压测以及多开一点 worker 进程了 |
6 pursuer Jun 25, 2025 @wudanyang 协程的作用就是协作执行,执行时间长(我假设说的 CPU 密集)还需要自动让出,那就是多线程的场景。实际多线程要考虑锁和线程安全,写线程安全代码未必比手动 await 简单。python 的线程是抢占式调度,但这个和协程无关,无论多少协程也都是跑在这个可以抢占调度的线程上的。 |
7 wyntalgeer Jun 25, 2025 内容跟标题没有丁点关系,看不出 async 为什么不能跑生产 人菜跟工具有什么关系,内容好似在问给 3 岁小孩玩菜刀一样 |
8 freemoon Jun 25, 2025 asyncio.to_thread() 用于处理同步函数。 |
9 unused Jun 25, 2025 没人规定用了协程就不能多线程了 |
10 wudanyang OP @wyntalgeer #7 看不出来吗?表达的是容易阻塞事件循环,导致线上延迟甚至挂掉 |
12 simple2025 Jun 25, 2025 @wudanyang 这个倒是确实会.但是你可以多开一些进程啊.把 async 把阻塞用吧,虽然理论不行,但是呢又不是不能用. |
13 simple2025 Jun 25, 2025 @wudanyang 我现在倒是觉得 python web 根本没有必要多线程,async/await 直接多进程走起,反正 linux 进程和线程的区别又不大,除非你起个上百个进程,估计影响不大,而且我也没有搜到, 开上百个进程与上百个线程之间的性能差距到底有多大,延迟有多高. |
14 simple2025 Jun 25, 2025 @chenqh 以前只是知道多线程比多进程性能,但是啊,差距有多少?没有人说啊.我现在都不明白我当时为什么会因为仅仅 好而去选择呢. |
15 capric Jun 25, 2025 3.7 就开始用在生产环境了,哪有什么大坑,稳的很 |
16 fds Jun 25, 2025 那么多人都在用……当然新手用不好也正常。卡主线程是很常见的 需要注意的问题,用 nodejs 也一样。你多写点测试看看阻塞没阻塞呗。 当然 python async 历史比较久,实现也多种多样,建议多读读官方文档 https://docs.python.org/3/library/asyncio-task.html#running-in-threads 你要接受不了不如换 go ,异步流程更清晰,性能也更好。当然处理 json 什么的就麻烦些。 |
17 Vegetable Jun 25, 2025 1. 你不 await ,函数不会执行,不存在阻塞的情况。 2. 三方库每做 async 改造,不小心用了这种情况令人难以置信,需要关注几乎就只有 io 一种场景。常用的操作都有专门的异步版本,很难想象这个场景有什么“不小心”。 3. 不理解 |
18 fds Jun 25, 2025 o 对 忘了说了,如果你要用 python 做 web ,那无论如何是不能放 cpu 密集的任务的,因为放在哪里都会卡住。这种还是找个任务队列或者就先临时存在数据库里,然后再另外启动一些后端服务处理。 |
19 w568w Jun 25, 2025 我总结你说的三点,其实都是一个问题:单线程的协程模型是否会被 内在(本身写的同步代码太复杂)或外在(调用了阻塞的第三方库)的 CPU 密集过程卡住事件循环? 答案是有可能,并且我遇到过不止一次了。 对策也是三个: 1. 取决于你 asyncio 的实现,是可以开多个 worker 线程进行池化调度的。并不是所有实现都是真的「单线程」。有一些高级运行时还有 work-stealing 之类的机制帮助多线程调度; 2. 人工检查可能卡住的地方,繁重任务放到新线程去做; 3. web 这种可能涉及高并发的情况,如果担心此类问题,还是用线程模型的框架较好。 |
20 111111111111 Jun 25, 2025 1. 协程才可以被 await ,而且必须要被 await ,否则会引发警告,并且不会阻塞,而是继续执行下一行代码 2. 异步代码里“写”或“调用”同步代码,这二者没区别,这个阻塞是预期内的,所以写代码(或选第三方库)的时候想清楚要同步、异步、还是混写,以及哪种异步 3. await 主要是用在 IO 时切换,就算数据多等待的时间长,但是不影响其他协程的运行,也不吃 CPU ,时间长就长呗,有啥好担心的 OP 提到确实是使用 aync 该要思考的,其实更多和人有关,而不是和 async 的设计有关 |
22 dcsuibian Jun 25, 2025 那 Python 直接使用多线程模型不行吗? |
25 zhuangzhuang1988 Jun 25, 2025 https://docs.python.org/zh-cn/3.13/library/asyncio-dev.html asyncio 官方还是有一些检查工具的. 我目前都是用的 anyio 有 async with group 不怕少 await. |
26 wudanyang OP @zhuangzhuang1988 #25 官网说法: > ## 运行阻塞的代码 > 不应该直接调用阻塞( CPU 绑定)代码。例如,如果一个函数执行 1 秒的 CPU 密集型计算,那么所有并发异步任> 务和 IO 操作都将延迟 1 秒。 > 可以用执行器在不同的线程甚至不同的进程中运行任务,以避免使用事件循环阻塞 OS 线程。 请参阅 loop.run_in_executor() 方法了解详情。 问题是,当前运行很快,有可能上线一段时间后需要执行 1 秒了 |
27 aloxaf Jun 25, 2025 你说的缺陷确实客观存在,甚至在很多语言中都存在。 但我不理解为啥存在这些缺陷就不能上生产环境了,难道有啥语言是完美无缺的、用它写的代码一定能 100% 无 BUG 运行? |
28 neoblackcap Jun 25, 2025 所有的问题其实都可以封装一层来解决,API 接口只做最简单的请求以及参数校验,然后直接将业务处理通过可控的异步方法发给后方的工作线程池或者任务队列。并发是上去了,但是会伴随着每个请求的延迟变高。 系统跟语言是两个不一样的东西,两者没有什么直接关系。比如提出的第二点,那么我说如果第三方库出 bug ,导致线程死锁了。这不也有风险嘛。这压根就不是什么语言导致的问题。 想构建一个健壮的系统,还得多测试,多几套备用方案。指望一个框架,一种语言解决全部问题,那么跟把它当成银弹有什么区别? |
29 luckyc Jun 25, 2025 多 worker 啊, php 不也是这样么? |
30 kazumaa Jun 25, 2025 vllm ,sglang 这些框架就是用 fastapi 啊,用的是 asyncio 。至于第三方库只有同步没有异步,完全可以使用 asyncio.to_thread 封装。而多个 coroutine 可以用 asyncio.gather 并发 |
31 ClericPy Jun 25, 2025 看懂了,也遇到过,例如 写了个 def 里面有 time.sleep 之类的同步阻塞函数,或者像 HtmlParser 这种计算密集型入参是个十几万字符的 HTML ,把 CPU 吃满了不释放,导致把主线程阻塞住,事件循环罢工了 没啥办法,一开始设计的时候就这样,mypy 、ruff 什么的有的能提示有的认不出来,至于大模型 code review 效果也一言难尽 林子大了什么鸟都有,代码长了见过的没见过的 bug 到处都是,遇到修吧 很多 route 不运行也发现不了它阻塞,以前 gevent 环境里被某个 mysql 的 C lib 阻塞住,也是找了一天才看到然后扔到 thread 里 很多 golang 玩家喜欢炫耀的就是他们家原生并发快乐,曾经尝试过模仿 golang 那套造轮子,结果不是出 bug 就是 pickle 失败,或者性能不增反降,最后:我都用 Python 了,慢点就慢点 |
32 574402766 Jun 25, 2025 这些问题是这一类协程实现都有的吧 不只是 python 的 asyncio 会出现 |
34 Vegetable Jun 25, 2025 @yh7gdiaYW 一个库如果不声明自己兼容异步,那就默认全都是阻塞。至于一个阻塞方法使用者怎么处理是自己的事情。我倒是没见过什么第三方库声称自己兼容异步但是实际上内部有阻塞操作。 |
35 flowkidtw Jun 25, 2025 我们团队在生产环境中使用了 asyncio ,目前没有遇到过稳定性问题。每种方案都可能有缺陷,是否可靠更多的还是取决于人。 |
36 molika Jun 25, 2025 一点问题都没有 . 线上跑的嗷嗷的. 访问量很大. 第一点 不会. 不 await 不会执行. 如果是耗费大量时间 的运算 说明 这个方法不适合 async 需要丢到多进程. 要知道 async 的适合的场景. 第二点 没办法. 尽量找到带 async. aio 的库 . 并且做好改造的准备. |
37 ManjusakaL Jun 25, 2025 via iPhone Node.js 能真的跑在生产上吗? |
38 LostPrayers Jun 25, 2025 跑不了一点,之前一个小服务用 python+fastapi 写的,真的不适合并发环境。 后来改回 golang 写服务,然后在 go 协程里 shell 执行 py 脚本了 |
39 9hills Jun 25, 2025 协程的体验中,我觉得 Golang 是最舒服的。 |
40 mayli Jun 26, 2025 那你这个是生态或者程序员的问题了 类似问题 python 当前真的能跑在生产环境吗? 程序员写的代码真的能跑在生产环境吗? 性能差的代码真的能跑在生产环境吗? py 问题是灵活性太大,程序员很容易踩坑,但是不代表一定会踩,2 3 的确会有问题,不过 2025 年了,库的 bug 少很多了。 |
41 fkdtz Jun 26, 2025 提出这样的问题,估计是没搞清楚协程适合于什么场景,甚至根本没理解什么是协程。 直接回答问题:能跑,不但跑的很好,而且效率非常高。 但前提是你真的理解协程在干嘛。 如果你理解了,那么几乎不会出现你说的忘了写 await 的情况,就像你定义函数时不会忘了写 def 一样,就算有 1%的概率你加班三天三夜没合眼真的忘了,那么运行起来必然出错,因为 awaitable 的对象必然不等于你期望的结果。 我觉得标题改成,不熟悉 Python 协程的情况下,能直接干生成环境吗?那么答案是不能。 |
42 shimada666 Jun 26, 2025 2 确实巨坑,之前用 openai 的同步 Client: OpenAI() 就把其他卡住了。改错了 AsyncOpenAI() 才正常 |
43 way2explore2 Jun 26, 2025 via Android 我有用啊,在生产环境 |
44 cj323 Jun 26, 2025 我的代码比较老,之前尝试过 asyncio 确实阻塞过,加上写法很繁琐+老代码又不支持。 后来 gevent.monkey_patch_all ,反倒没出过事。 |
45 cj323 Jun 26, 2025 CPU 密集的 py 代码我会尽量避免 asyncio ,写着舒服多了。这种代码也往往本身也会接 message queue ,就天然 async 了。 |
46 conn457567 Jun 26, 2025 via Android 相反我认为协程是 web 领域的趋势 |
47 wangritian Jun 26, 2025 让我想到 php-swoole ,早期的协程存在一模一样的问题,后来仿照 go 加入了抢占式调度 go 的混合协程确实很棒 |
48 wangtian2020 Jun 26, 2025 公司用 python 写后端,新来的同事接口异步同步的问题调了几天了才勉强能用。不如 nodejs 异步单线程一根毛,nodejs 想阻塞都难 |
49 user1284 Jun 26, 2025 诶, 你忘了写单测不就通过不了吗? 这为什么会发现不了, 还是说你写好一个功能完全不测试就上正式服了? |
50 crackidz Jun 26, 2025 第一个没有 await 可以用 type check 工具检查出来的,如果你本地使用或者 CI pipeline 里有,很容易可以检查出来。实在不行,你可以从日志捕获里抓 was never awaited 这个 Warning 也能发现 第二个是确确实实的问题,没什么很好的办法解决。选库和与别人协作的时候巨容易踩的一个坑,目前我们是加响应时间监控解决的,存在有超长时间返回的请求都需要确认一下是不是有性能问题 第三个应该和哪种都没关系,除非不用 Python 或者多进程... 或者等 Python 3.14 以后版本的 free threading 来解决 |
51 sazima Jun 26, 2025 第二个,没适配的方法放线程池执行 |
52 penzi Jun 26, 2025 python 就不应该出现在生产环境 |
53 yh7gdiaYW Jun 26, 2025 @Vegetable 看来你并没有公司级的 python 生产经验,不过现代确实也没多少公司用 python 搞 web...开源的库都好说,公司私有 pypi 的库就没那么老实了 |
54 sharpless Jun 26, 2025 啧啧,一说到编程语言,就非争个高下,不适合这个 不适合那个,这个不行那个不行,来各位讲讲自己用自己的梦中情语搞出了多大的项目吧,日活、并发、业绩 |
55 julyclyde Jun 26, 2025 忘了写 await 的话连返回类型都不对,变成返回一个 coroutine 了 你能不能先写几句再来提问啊? |
56 Vegetable Jun 26, 2025 @yh7gdiaYW 你这么说还挺武断的,我做的挺多的。 内部库默认是自己的代码,既然你已经引用了,证明这东西要不就是属于你自己的版本,要不就是已经上线验证过,总之是获得了自己的信任才会踩坑。 清楚自己引用的方法会不会产生 IO,是否兼容异步,是编写异步代码最基本的要求,这方面的不小心根本是不可原谅的,没有办法将责任推给其他人。 |
57 bitmin Jun 26, 2025 还行吧,我们小小公司每天流水几百万的小小项目正在用着,啥语言啥框架能没坑呢 |
58 clemente Jun 26, 2025 升级到 python3.14 这个问题就不用你解决了 |
59 dzdh Jun 26, 2025 为啥没有 vue native 呢 |
61 wudanyang OP @LostPrayers #38 请问是遇到什么问题才换的 golang |
63 GetMoneyMan Jun 26, 2025 用了 asyncio ,就得有这个觉悟,整个团队的技术水平和规范都得跟上。简单说,Python 的 asyncio 就是个残废。需要你自己手动挡换挡,一不留神就熄火。Go 的 goroutine 才是真正的自动挡。 |
65 julyclyde Jun 26, 2025 按你的附言所说的情况 如那个语句真的是能占满 cpu ,那我觉得不让也行啊。干哪个活不是干啊,何必厚此薄彼 |
66 xchange Jun 26, 2025 很难想象这是 2025 年的问题 |
68 wudanyang OP @GetMoneyMan #63 很形象了 |
69 wudanyang OP |
71 wudanyang OP @julyclyde #70 一种是 cpu 时间片占满不释放,导致其他请求延迟,导致并发升高,从而导致整个服务崩掉。 还有一种是 cpu 时间片一段时间后被抢占,其他请求虽然有一些影响但是还是会比较快返回。 |
72 wudanyang OP 看了一下 rust 的 tokio 协程实现。 虽然也是协作式调度,但其原生支持 flavor = "multi_thread" 多线程调度器,支持工作窃取。 还有调度预算机制,用于防止单个任务长期占用线程,从而造成其他任务饿死或延迟。 tokio 不能避免阻塞,只是更加安全一些。 早期 Go 1.14 之前的调度器相当于 协作式调度,协程只在特定时机(如函数调用、通道操作、`time.Sleep`等)主动让出 CPU 。死循环中若没有这些操作,调度器无法抢占执行权。 Go 1.14 之后调度器引入了基于信号抢占 ,即使协程执行死循环,操作系统会向运行该协程的线程发送信号抢占执行权。 |