我来说说异步框架的最大缺点

2021-04-20 16:04:09 +08:00
 balabalaguguji

异步大家都在夸,都在说他的好处,但是似乎没人说过他的最大缺点,我来说说吧,避免踩坑。

异步因为是只有一个线程,如果有一个地方阻塞了,那整个网站全部都卡住了(多进程的另说),所以你得时刻记得,如果会阻塞的方法,就得用异步的库。另外还得确保别写出死循环的逻辑,不然也是卡住整个站。

异步现在支持最好的应该是 nodejs 吧,各种异步库都有,但是 python 的支持就少很多了,如果用 gevent,猴子补丁不能帮你把所有的接口都补成异步的,所以你得清楚什么方法是可以用的,例如 commands 这个就不能用,没打补丁,可以改用 subprocess 。

写异步的代码时得时刻提醒自己以上问题,但是如果用多线程模型,就不用担心这些,如果你的网站不是特别大的访问量,可以使用多线程模型,够简单;如果是需要高并发,有大量用户,可以用异步框架并始终记住不要用阻塞方法。

如果说得不对的地方,请大家指点。

16162 次点击
所在节点    编程
153 条回复
no1xsyzy
2021-04-21 15:47:24 +08:00
@guyeu #125 这两个缺陷其实都是消极非抢占式调度的问题。
激进非抢占式调度不会导致关键词传染,因为可以通过二次编译,在各种位置打上 await sleep(0)
但可能导致一些依赖于同步行为的、非线程安全的代码产生异状行为,甚至难以排查的怪异问题。
目前我所知的激进非抢占调度都是函数式语言
masterclock
2021-04-21 16:01:33 +08:00
讲异步、IO 方面,应该看看 scala 里 zio,fs2 框架。
比如 zio:
ZIO is a library for asynchronous and concurrent programming that is based on pure functional programming.
结合 scala “默认”不可变对象,lazy 等特性,写代码完全没有焦虑感,连 rust 都不想要了。
wuwukai007
2021-04-21 17:29:09 +08:00
@balabalaguguji rpc 可以走 http 的,把没办法异步的库和逻辑 放在 单独的 rpc 服务里面,主服务通过 aiohttp 调用,不就曲线异步了吗
ykb8121
2021-04-21 17:59:34 +08:00
说的怕不是异步的缺点,而是自己代码菜的缺点...
都用异步了还写阻塞方法,居然还写得出死循环,那这本身就是程序员自己代码的问题,异步这锅不背

另:py 生态正逐渐完善,大多数应用场景想找其实都能找到。再说手动改异步,除了费时间,远没有你想象的困难,望多学习
tianyamomo
2021-04-21 18:38:17 +08:00
异步代码是不应该出现阻塞代码的
balabalaguguji
2021-04-21 18:40:44 +08:00
@tianyamomo #145 这不正是我提醒的
balabalaguguji
2021-04-21 18:41:35 +08:00
@ykb8121 #144 学会看文字理解别人意思,少做喷子
iseki
2021-04-21 19:28:11 +08:00
@balabalaguguji #97 不会吧,don't block me 不是异步大多数有类似问题的非阻塞异步模型重点强调的吗
guyeu
2021-04-21 20:12:38 +08:00
@no1xsyzy #141 激进非抢占和消极非抢占都是什么意思呀,第一次听到这俩名词,搜了一下也没搜到,能给个链接学习一个吗
AlexEzio
2021-04-21 20:26:24 +08:00
异步编程本身就和多线程编程,多进程编程属于并发编程领域的不同范式。编程时需要注意代码是否是阻塞,就像多线程需要注意各类资源安全问题, 多进程需要考虑资源共享问题。 不同范式,在编程时考虑的关注点是不一样的,这并不是异步的缺点,恰好是其特性;只是异步编程本身调试起来就比较麻烦,很多人没有深入了解过,肯定是会经常阻塞程序的。

python 你应该了解的并不多。
cpython 由于 GIL 的存在,多线程模型下,往往只能使用单核的计算资源,因此通过多线程来解决 c10k 问题并不现实,所以在早期才有 twist, gevent 这种第三方的异步库.
python3.5 原生实现了异步,加入了 async/await 关键字。3.7 使用 c 实现了异步库,社区对各种基本中间件的异步支持也非常好,并不存在你说的支持少的情况。
go 的优势不需要过多学习,掌握了基础库使用后,就可以写出性能很好的程序. 而不需要关心过多关心底层的调度,并发之类的,这些基础库都帮你做好了。
no1xsyzy
2021-04-22 00:52:34 +08:00
@guyeu 没有,我只是感性地描述了两种(实际上是光谱)非抢占式调度的差异。
像 js python 之流,你需要业务代码手动打 await 桩,感知上就是关键词的传染性,这种消极的、将具体的问题下流给业务代码的、Worse is better 哲学的,最终增加了问题的复杂性。
另一侧来说,打个极端的比方,实质上执行时将每句指令、每个表达式都看作是 await 的,看上去就比需要业务代码手动打 await 桩要激进得多。作为例子,我听说过 Erlang 会在导入代码的时候在各种地方打上调度用的桩,结果就是同步语法,异步语义。天然采用类似 CSP 的 Haskell Monad >>= 也可以实际视同非抢占式调度来执行。
中间其实还有一些不导致传染的非抢占式调度,比如 call/cc 的方式,本质还是 CSP,问题 1 的语言层面的问题已经充分解决过好几遍了,之所以没有被广泛利用,主要是现有代码的兼容性会有问题。
至于问题 2,其实是非抢占式的问题,除非做到极端激进,上述 Erlang 那样,简直等于抢占式调度。不然的话大概必须靠优先级队列来调度了,如何设置优先级又是一个问题。
no1xsyzy
2021-04-22 01:06:36 +08:00
@ykb8121 《 C 陷阱与缺陷》不知道你是否听说过这本书。
语言设计和具体实现层面上有问题,甚至是不能动的历史遗留问题,导致写起来有心智负担,确实是这些工具有问题。
但这确实不是异步的问题,而是语言设计和实现的问题。
照道理,应当从语言层面上保证,根本无法写出阻塞代码,这个简单,把所有外部函数全部赌死;并且可以从实现层面上保证,死循环不应当造成单线程异步卡死,这个其实也简单,只要每当发生循环(从字节码上判断,可以发现有条件或无条件地向低地址跳转),就进行一次让步。

或者,更激进一点。某个语言的官方设计和官方实现中,超过千万的循环就相当可能导致 kernel kill (OOM),并且官方文档明确地指出这一点是语言的设计目标之一带来的一个副作用。这也是程序员的问题吗?
MaxJin
2021-04-22 15:17:11 +08:00
@balabalaguguji 对是这鸭子

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/771935

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX