V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
wudanyang
V2EX  ›  Python

Python 的 async 协程当前真的能跑在生产环境吗?

  •  
  •   wudanyang ·
    wudanyang6 · 17 天前 · 5905 次点击

    python async 启动协程时,如果当前协程不主动 await 释放,那就会一直占用事件循环,其他协程无法执行。

    能想到的一些非常有风险的点:

    1. 程序员忘了写 await ,单纯靠 cr 很难完全发现,导致阻塞
    2. python 第三方库没有做 async 改造,不小心用了,导致阻塞
    3. 有可能本地运行时间短,到了线上数据量大了,阻塞时间变长

    问了 ds 和 chatgpt , 看起来都没有很好的解决方案

    第 1 条附言  ·  17 天前
    第一条的意思是,程序员写了一个 cpu 密集型语句,且执行时间长,但是中间没有让出时间片,导致其他协程阻塞了。
    73 条回复    2025-06-30 15:49:58 +08:00
    pursuer
        1
    pursuer  
       17 天前   ❤️ 3
    第一条 python 的 await 如果写漏了,协程是不会运行的,第二条没改造的第三方库可以丢给其他线程跑,第三条和数据量大用不用协程都存在的一样的问题
    chenqh
        2
    chenqh  
       17 天前
    可以跑,阻塞就阻塞,你用 supervisor 多起几个进程就行了.虽然这种情况下有没有 async 都一样了.
    wudanyang
        3
    wudanyang  
    OP
       17 天前
    @pursuer #1 第一条的意思是,程序员写了一个执行时间长的语句,但是中间没有使用 await 让出执行。
    第二条确实可以放到线程执行,但 python 现在异步生态没这么好,很多都需要线程执行了
    第三条如果直接用线程的话,线程是抢占式调度,不会让一个线程一直占着 cpu ,但是协程遇到数据量增长的情况,占用的 cpu 就会越来越多
    wudanyang
        4
    wudanyang  
    OP
       17 天前
    @chenqh #2 如果启动的是 web server ,同时来两个请求,耗时本来都很短。
    这时候如果其中一个请求阻塞了,那另一个请求的耗时也跟着变长了。
    yh7gdiaYW
        5
    yh7gdiaYW  
       17 天前   ❤️ 1
    第二点是巨坑,光靠 review 很难 100%发现,只能结合压测以及多开一点 worker 进程了
    pursuer
        6
    pursuer  
       17 天前
    @wudanyang 协程的作用就是协作执行,执行时间长(我假设说的 CPU 密集)还需要自动让出,那就是多线程的场景。实际多线程要考虑锁和线程安全,写线程安全代码未必比手动 await 简单。python 的线程是抢占式调度,但这个和协程无关,无论多少协程也都是跑在这个可以抢占调度的线程上的。
    wyntalgeer
        7
    wyntalgeer  
       17 天前   ❤️ 2
    内容跟标题没有丁点关系,看不出 async 为什么不能跑生产

    人菜跟工具有什么关系,内容好似在问给 3 岁小孩玩菜刀一样
    lasuar
        8
    lasuar  
       17 天前
    asyncio.to_thread() 用于处理同步函数。
    unused
        9
    unused  
       17 天前
    没人规定用了协程就不能多线程了
    wudanyang
        10
    wudanyang  
    OP
       17 天前
    @wyntalgeer #7 看不出来吗?表达的是容易阻塞事件循环,导致线上延迟甚至挂掉
    wudanyang
        11
    wudanyang  
    OP
       17 天前
    @unused #9 问题是就算是混用,其中协程的部分也容易出现上面的问题,导致阻塞
    chenqh
        12
    chenqh  
       17 天前
    @wudanyang 这个倒是确实会.但是你可以多开一些进程啊.把 async 把阻塞用吧,虽然理论不行,但是呢又不是不能用.
    chenqh
        13
    chenqh  
       17 天前
    @wudanyang 我现在倒是觉得 python web 根本没有必要多线程,async/await 直接多进程走起,反正 linux 进程和线程的区别又不大,除非你起个上百个进程,估计影响不大,而且我也没有搜到, 开上百个进程与上百个线程之间的性能差距到底有多大,延迟有多高.
    chenqh
        14
    chenqh  
       17 天前
    @chenqh 以前只是知道多线程比多进程性能,但是啊,差距有多少?没有人说啊.我现在都不明白我当时为什么会因为仅仅
    好而去选择呢.
    capric
        15
    capric  
       17 天前
    3.7 就开始用在生产环境了,哪有什么大坑,稳的很
    fds
        16
    fds  
       17 天前
    那么多人都在用……当然新手用不好也正常。卡主线程是很常见的 需要注意的问题,用 nodejs 也一样。你多写点测试看看阻塞没阻塞呗。
    当然 python async 历史比较久,实现也多种多样,建议多读读官方文档
    https://docs.python.org/3/library/asyncio-task.html#running-in-threads

    你要接受不了不如换 go ,异步流程更清晰,性能也更好。当然处理 json 什么的就麻烦些。
    Vegetable
        17
    Vegetable  
       17 天前   ❤️ 2
    1. 你不 await ,函数不会执行,不存在阻塞的情况。
    2. 三方库每做 async 改造,不小心用了——这种情况令人难以置信,需要关注几乎就只有 io 一种场景。常用的操作都有专门的异步版本,很难想象这个场景有什么“不小心”。
    3. 不理解
    fds
        18
    fds  
       17 天前
    o 对 忘了说了,如果你要用 python 做 web ,那无论如何是不能放 cpu 密集的任务的,因为放在哪里都会卡住。这种还是找个任务队列或者就先临时存在数据库里,然后再另外启动一些后端服务处理。
    w568w
        19
    w568w  
       17 天前   ❤️ 1
    我总结你说的三点,其实都是一个问题:单线程的协程模型是否会被 内在(本身写的同步代码太复杂)或外在(调用了阻塞的第三方库)的 CPU 密集过程卡住事件循环?

    答案是有可能,并且我遇到过不止一次了。

    对策也是三个:

    1. 取决于你 asyncio 的实现,是可以开多个 worker 线程进行池化调度的。并不是所有实现都是真的「单线程」。有一些高级运行时还有 work-stealing 之类的机制帮助多线程调度;

    2. 人工检查可能卡住的地方,繁重任务放到新线程去做;

    3. web 这种可能涉及高并发的情况,如果担心此类问题,还是用线程模型的框架较好。
    111111111111
        20
    111111111111  
       17 天前   ❤️ 1
    1. 协程才可以被 await ,而且必须要被 await ,否则会引发警告,并且不会阻塞,而是继续执行下一行代码

    2. 异步代码里“写”或“调用”同步代码,这二者没区别,这个阻塞是预期内的,所以写代码(或选第三方库)的时候想清楚要同步、异步、还是混写,以及哪种异步

    3. await 主要是用在 IO 时切换,就算数据多等待的时间长,但是不影响其他协程的运行,也不吃 CPU ,时间长就长呗,有啥好担心的


    OP 提到确实是使用 aync 该要思考的,其实更多和人有关,而不是和 async 的设计有关
    w568w
        21
    w568w  
       17 天前
    @w568w 另外协程的这些问题不影响吞吐量,只影响延迟。所以说其实只有延迟敏感的应用才需要担心
    dcsuibian
        22
    dcsuibian  
       17 天前
    那 Python 直接使用多线程模型不行吗?
    wudanyang
        23
    wudanyang  
    OP
       17 天前
    @w568w #21 是的,如果是 web server ,其中一个请求阻塞了 1 分钟,其他请求都会跟着阻塞 1 分钟
    wudanyang
        24
    wudanyang  
    OP
       17 天前
    @dcsuibian #22 也是可以的
    zhuangzhuang1988
        25
    zhuangzhuang1988  
       17 天前   ❤️ 1
    https://docs.python.org/zh-cn/3.13/library/asyncio-dev.html
    asyncio 官方还是有一些检查工具的.

    我目前都是用的 anyio
    有 async with group 不怕少 await.
    wudanyang
        26
    wudanyang  
    OP
       17 天前
    @zhuangzhuang1988 #25

    官网说法:

    > ## 运行阻塞的代码
    > 不应该直接调用阻塞( CPU 绑定)代码。例如,如果一个函数执行 1 秒的 CPU 密集型计算,那么所有并发异步任> 务和 IO 操作都将延迟 1 秒。

    > 可以用执行器在不同的线程甚至不同的进程中运行任务,以避免使用事件循环阻塞 OS 线程。 请参阅 loop.run_in_executor() 方法了解详情。

    问题是,当前运行很快,有可能上线一段时间后需要执行 1 秒了
    aloxaf
        27
    aloxaf  
       17 天前
    你说的缺陷确实客观存在,甚至在很多语言中都存在。

    但我不理解为啥存在这些缺陷就不能上生产环境了,难道有啥语言是完美无缺的、用它写的代码一定能 100% 无 BUG 运行?
    neoblackcap
        28
    neoblackcap  
       17 天前
    所有的问题其实都可以封装一层来解决,API 接口只做最简单的请求以及参数校验,然后直接将业务处理通过可控的异步方法发给后方的工作线程池或者任务队列。并发是上去了,但是会伴随着每个请求的延迟变高。

    系统跟语言是两个不一样的东西,两者没有什么直接关系。比如提出的第二点,那么我说如果第三方库出 bug ,导致线程死锁了。这不也有风险嘛。这压根就不是什么语言导致的问题。

    想构建一个健壮的系统,还得多测试,多几套备用方案。指望一个框架,一种语言解决全部问题,那么跟把它当成银弹有什么区别?
    l4ever
        29
    l4ever  
       17 天前
    多 worker 啊, php 不也是这样么?
    xuwen
        30
    xuwen  
       17 天前
    vllm ,sglang 这些框架就是用 fastapi 啊,用的是 asyncio 。至于第三方库只有同步没有异步,完全可以使用 asyncio.to_thread 封装。而多个 coroutine 可以用 asyncio.gather 并发
    ClericPy
        31
    ClericPy  
       17 天前   ❤️ 1
    看懂了,也遇到过,例如

    写了个 def 里面有 time.sleep 之类的同步阻塞函数,或者像 HtmlParser 这种计算密集型入参是个十几万字符的 HTML ,把 CPU 吃满了不释放,导致把主线程阻塞住,事件循环罢工了

    没啥办法,一开始设计的时候就这样,mypy 、ruff 什么的有的能提示有的认不出来,至于大模型 code review 效果也一言难尽

    林子大了什么鸟都有,代码长了见过的没见过的 bug 到处都是,遇到修吧

    很多 route 不运行也发现不了它阻塞,以前 gevent 环境里被某个 mysql 的 C lib 阻塞住,也是找了一天才看到然后扔到 thread 里

    很多 golang 玩家喜欢炫耀的就是他们家原生并发快乐,曾经尝试过模仿 golang 那套造轮子,结果不是出 bug 就是 pickle 失败,或者性能不增反降,最后:我都用 Python 了,慢点就慢点
    574402766
        32
    574402766  
       17 天前
    这些问题是这一类协程实现都有的吧 不只是 python 的 asyncio 会出现
    yh7gdiaYW
        33
    yh7gdiaYW  
       17 天前
    @Vegetable 你没法直到哪个第三方库间接旧用了非 async 的 io ,所以说是巨坑,除非你的项目简单没这么多层依赖
    Vegetable
        34
    Vegetable  
       17 天前   ❤️ 1
    @yh7gdiaYW 一个库如果不声明自己兼容异步,那就默认全都是阻塞。至于一个阻塞方法使用者怎么处理是自己的事情。我倒是没见过什么第三方库声称自己兼容异步但是实际上内部有阻塞操作。
    flowkidtw
        35
    flowkidtw  
       17 天前
    我们团队在生产环境中使用了 asyncio ,目前没有遇到过稳定性问题。每种方案都可能有缺陷,是否可靠更多的还是取决于人。
    molika
        36
    molika  
       17 天前
    一点问题都没有 . 线上跑的嗷嗷的. 访问量很大.
    第一点 不会. 不 await 不会执行. 如果是耗费大量时间 的运算 说明 这个方法不适合 async 需要丢到多进程. 要知道 async 的适合的场景.
    第二点 没办法. 尽量找到带 async. aio 的库 . 并且做好改造的准备.
    ManjusakaL
        37
    ManjusakaL  
       17 天前 via iPhone
    Node.js 能真的跑在生产上吗?
    LostPrayers
        38
    LostPrayers  
       17 天前
    跑不了一点,之前一个小服务用 python+fastapi 写的,真的不适合并发环境。
    后来改回 golang 写服务,然后在 go 协程里 shell 执行 py 脚本了
    9hills
        39
    9hills  
       17 天前
    协程的体验中,我觉得 Golang 是最舒服的。
    mayli
        40
    mayli  
       17 天前   ❤️ 1
    那你这个是生态或者程序员的问题了

    类似问题

    python 当前真的能跑在生产环境吗?
    程序员写的代码真的能跑在生产环境吗?
    性能差的代码真的能跑在生产环境吗?

    py 问题是灵活性太大,程序员很容易踩坑,但是不代表一定会踩,2 3 的确会有问题,不过 2025 年了,库的 bug 少很多了。
    fkdtz
        41
    fkdtz  
       17 天前   ❤️ 1
    提出这样的问题,估计是没搞清楚协程适合于什么场景,甚至根本没理解什么是协程。

    直接回答问题:能跑,不但跑的很好,而且效率非常高。

    但前提是你真的理解协程在干嘛。

    如果你理解了,那么几乎不会出现你说的忘了写 await 的情况,就像你定义函数时不会忘了写 def 一样,就算有 1%的概率你加班三天三夜没合眼真的忘了,那么运行起来必然出错,因为 awaitable 的对象必然不等于你期望的结果。

    我觉得标题改成,不熟悉 Python 协程的情况下,能直接干生成环境吗?那么答案是不能。

    🙏
    shimada666
        42
    shimada666  
       17 天前
    2 确实巨坑,之前用 openai 的同步 Client: OpenAI() 就把其他卡住了。改错了 AsyncOpenAI() 才正常
    way2explore2
        43
    way2explore2  
       17 天前 via Android
    我有用啊,在生产环境
    cj323
        44
    cj323  
       17 天前
    我的代码比较老,之前尝试过 asyncio 确实阻塞过,加上写法很繁琐+老代码又不支持。

    后来 gevent.monkey_patch_all ,反倒没出过事。
    cj323
        45
    cj323  
       17 天前
    CPU 密集的 py 代码我会尽量避免 asyncio ,写着舒服多了。这种代码也往往本身也会接 message queue ,就天然 async 了。
    conn457567
        46
    conn457567  
       17 天前 via Android
    相反我认为协程是 web 领域的趋势
    wangritian
        47
    wangritian  
       17 天前   ❤️ 1
    让我想到 php-swoole ,早期的协程存在一模一样的问题,后来仿照 go 加入了抢占式调度
    go 的混合协程确实很棒
    wangtian2020
        48
    wangtian2020  
       17 天前
    公司用 python 写后端,新来的同事接口异步同步的问题调了几天了才勉强能用。不如 nodejs 异步单线程一根毛,nodejs 想阻塞都难
    yb2313
        49
    yb2313  
       17 天前
    诶, 你忘了写单测不就通过不了吗? 这为什么会发现不了, 还是说你写好一个功能完全不测试就上正式服了?
    crackidz
        50
    crackidz  
       17 天前
    第一个没有 await 可以用 type check 工具检查出来的,如果你本地使用或者 CI pipeline 里有,很容易可以检查出来。实在不行,你可以从日志捕获里抓 was never awaited 这个 Warning 也能发现
    第二个是确确实实的问题,没什么很好的办法解决。选库和与别人协作的时候巨容易踩的一个坑,目前我们是加响应时间监控解决的,存在有超长时间返回的请求都需要确认一下是不是有性能问题
    第三个应该和哪种都没关系,除非不用 Python 或者多进程... 或者等 Python 3.14 以后版本的 free threading 来解决
    sazima
        51
    sazima  
       17 天前
    第二个,没适配的方法放线程池执行
    maggch97
        52
    maggch97  
       16 天前
    python 就不应该出现在生产环境
    yh7gdiaYW
        53
    yh7gdiaYW  
       16 天前
    @Vegetable 看来你并没有公司级的 python 生产经验,不过现代确实也没多少公司用 python 搞 web...开源的库都好说,公司私有 pypi 的库就没那么老实了
    sharpless
        54
    sharpless  
       16 天前
    啧啧,一说到编程语言,就非争个高下,不适合这个 不适合那个,这个不行那个不行,来各位讲讲自己用自己的梦中情语搞出了多大的项目吧,日活、并发、业绩
    julyclyde
        55
    julyclyde  
       16 天前
    忘了写 await 的话连返回类型都不对,变成返回一个 coroutine 了

    你能不能先写几句再来提问啊?
    Vegetable
        56
    Vegetable  
       16 天前   ❤️ 1
    @yh7gdiaYW 你这么说还挺武断的,我做的挺多的。
    内部库默认是自己的代码,既然你已经引用了,证明这东西要不就是属于你自己的版本,要不就是已经上线验证过,总之是获得了自己的信任才会踩坑。
    清楚自己引用的方法会不会产生 IO,是否兼容异步,是编写异步代码最基本的要求,这方面的不小心根本是不可原谅的,没有办法将责任推给其他人。
    bitmin
        57
    bitmin  
       16 天前
    还行吧,我们小小公司每天流水几百万的小小项目正在用着,啥语言啥框架能没坑呢
    clemente
        58
    clemente  
       16 天前
    升级到 python3.14 这个问题就不用你解决了
    dzdh
        59
    dzdh  
       16 天前
    为啥没有 vue native 呢
    wudanyang
        60
    wudanyang  
    OP
       16 天前
    @julyclyde #55 是我描述错误,可以看下附言
    wudanyang
        61
    wudanyang  
    OP
       16 天前
    @LostPrayers #38 请问是遇到什么问题才换的 golang
    wudanyang
        62
    wudanyang  
    OP
       16 天前
    @cj323 #44 感谢,研究下 gevent
    GetMoneyMan
        63
    GetMoneyMan  
       16 天前
    用了 asyncio ,就得有这个觉悟,整个团队的技术水平和规范都得跟上。简单说,Python 的 asyncio 就是个残废。需要你自己手动挡换挡,一不留神就熄火。Go 的 goroutine 才是真正的自动挡。
    wudanyang
        64
    wudanyang  
    OP
       16 天前
    @clemente #58 怎么说?
    julyclyde
        65
    julyclyde  
       16 天前
    按你的附言所说的情况
    如那个语句真的是能占满 cpu ,那我觉得不让也行啊。干哪个活不是干啊,何必厚此薄彼
    xchange
        66
    xchange  
       16 天前   ❤️ 1
    很难想象这是 2025 年的问题
    wudanyang
        67
    wudanyang  
    OP
       16 天前
    @julyclyde #65 如果对响应时间敏感的服务呢?
    wudanyang
        68
    wudanyang  
    OP
       16 天前
    @GetMoneyMan #63 很形象了
    wudanyang
        69
    wudanyang  
    OP
       16 天前
    @julyclyde #65 如果是纯多线程,系统会自动切线程执行,不会让一个线程把整个系统全阻塞住

    不过看起来 python 的协程会
    julyclyde
        70
    julyclyde  
       15 天前
    @wudanyang 都把 cpu 占满了还有脸说时间敏感?
    扩容啊!!
    wudanyang
        71
    wudanyang  
    OP
       15 天前
    @julyclyde #70 一种是 cpu 时间片占满不释放,导致其他请求延迟,导致并发升高,从而导致整个服务崩掉。
    还有一种是 cpu 时间片一段时间后被抢占,其他请求虽然有一些影响但是还是会比较快返回。
    wudanyang
        72
    wudanyang  
    OP
       15 天前
    看了一下 rust 的 tokio 协程实现。
    虽然也是协作式调度,但其原生支持 flavor = "multi_thread" 多线程调度器,支持工作窃取。
    还有调度预算机制,用于防止单个任务长期占用线程,从而造成其他任务饿死或延迟。
    tokio 不能避免阻塞,只是更加安全一些。

    早期 Go 1.14 之前的调度器相当于 协作式调度,协程只在特定时机(如函数调用、通道操作、`time.Sleep`等)主动让出 CPU 。死循环中若没有这些操作,调度器无法抢占执行权。
    Go 1.14 之后调度器引入了基于信号抢占 ,即使协程执行死循环,操作系统会向运行该协程的线程发送信号抢占执行权。
    mt3925
        73
    mt3925  
       12 天前
    @wudanyang 用协程也是要多进程部署的,单个进程只能用到一个 cpu ;单个进程里的协程占者 cpu 不放,不同进程还是会抢占啊
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2467 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 01:58 · PVG 09:58 · LAX 18:58 · JFK 21:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.