异步大家都在夸,都在说他的好处,但是似乎没人说过他的最大缺点,我来说说吧,避免踩坑。
异步因为是只有一个线程,如果有一个地方阻塞了,那整个网站全部都卡住了(多进程的另说),所以你得时刻记得,如果会阻塞的方法,就得用异步的库。另外还得确保别写出死循环的逻辑,不然也是卡住整个站。
异步现在支持最好的应该是 nodejs 吧,各种异步库都有,但是 python 的支持就少很多了,如果用 gevent,猴子补丁不能帮你把所有的接口都补成异步的,所以你得清楚什么方法是可以用的,例如 commands 这个就不能用,没打补丁,可以改用 subprocess 。
写异步的代码时得时刻提醒自己以上问题,但是如果用多线程模型,就不用担心这些,如果你的网站不是特别大的访问量,可以使用多线程模型,够简单;如果是需要高并发,有大量用户,可以用异步框架并始终记住不要用阻塞方法。
如果说得不对的地方,请大家指点。
1
mongodb 2021-04-20 16:05:28 +08:00 3
“但是 python 的支持就少很多了”
不是这样的。 |
2
abersheeran 2021-04-20 16:07:22 +08:00 1
我觉得你不要把协程和异步 IO 混在一起比较好。
|
3
balabalaguguji OP @mongodb #1 可能是我没找到或者比较难找。另外 redis 和 mongodb 这些的异步库感觉都不好用,都是回调的,写起来不方便
|
4
GM 2021-04-20 16:07:48 +08:00
这个确实是最大的问题,一处异步,全链路被迫全部异步,一不小心就 over,对团队的要求相应变高。
|
5
Jooooooooo 2021-04-20 16:08:11 +08:00
|
6
LeeReamond 2021-04-20 16:09:07 +08:00 17
很显然如果如你所言的“缺点”确实发生,那么意味着你连自己写的工程之中实现细节都无法把控,哪些接口是同步异步搞不清楚,且还有可能出现“死循环逻辑”。这种情况这边的建议是不要做工程,而不是说异步有什么缺点...
|
7
leopod1995 2021-04-20 16:09:48 +08:00 3
建议百度
`怎样理解阻塞非阻塞与同步异步的区别?` |
8
Ariver 2021-04-20 16:12:11 +08:00 via iPhone
@leopod1995 +1
|
9
janxin 2021-04-20 16:13:05 +08:00
看标题就猜到是 Python 2333
|
10
balabalaguguji OP @LeeReamond #6 相比多线程,这个对代码要求提高了很多,人员素质也要求提高,团队的每个人都得清楚自己写的会不会阻塞。另外有时候方法调用来调用去,是有可能出现死循环调用的,不是你理解的直接一个 while true 。
|
11
wanguorui123 2021-04-20 16:14:50 +08:00
异步最大的问题就是:
1 、同步循环容易阻塞主线程,需要用异步的思维来处理循环计算 2 、所有地方都要写成异步,不然容易混乱 |
12
balabalaguguji OP @GM #4 是这个意思
|
13
balabalaguguji OP @wanguorui123 #11 对,但是我以前没见过有人说过这些注意点,我自己踩过坑才知道,所以对人员要求是高了的
|
14
Te11UA 2021-04-20 16:17:12 +08:00
的确是,所以公司的项目还是多线程,异步的话是一个两个人单兵 run 的
|
15
CantSee 2021-04-20 16:17:56 +08:00
异步非阻塞?
|
16
balabalaguguji OP @Te11UA #14 嗯,如果不是必须异步,多线程挺适合
|
17
LeeReamond 2021-04-20 16:21:31 +08:00 5
@balabalaguguji 相比于多线程,人员素质要求高,并非引战,不过如果你是写 python 的人大概不知道自己在说什么。相比于使用 c/cpp/java 等语言的工程人员,使用多线程处理 IO 对人员素质的要求远比单线程异步高得多,线程安全需要解决有序性、可见性和原子性的问题,而单线程异步只需要理解 epoll 接口怎么调用,之后就是事件循环内部维护的事务了,如果你连这点要求都认为是高要求,我的建议仍然是不要写码。至于你说的调用产生死循环的问题,我司前一段刚做过微服务的栈深度清理,我确实是不理解你在一个线程内部还能搞出所谓调来调去?
|
18
Justin13 2021-04-20 16:22:20 +08:00 via Android
你说的阻塞逻辑也好,死循环也罢,都是不应该出现的问题代码,和你同步异步没有关系
|
19
Richard14 2021-04-20 16:24:59 +08:00 1
|
20
balabalaguguji OP @LeeReamond #17 应该是你技术方面比较厉害,考虑面比较全,线程方便考虑那么多处理。日常我们都很少需要考虑线程安全,按照一个团队来考虑,人员技术不一样,个人觉得用多线程会比异步的技术要求低。
|
21
balabalaguguji OP @LeeReamond #17 或许你不用跟线程比较,就说写异步代码最需要注意的是什么,是不是我说的这个。
|
22
shuax 2021-04-20 16:38:48 +08:00
等一个:“我来说说多线程框架的最大缺点”
|
23
qW7bo2FbzbC0 2021-04-20 16:39:55 +08:00
楼主说的是 func()
或者 await XXXAsync() 这种异步阻塞 想要的是 go func() 或者 XXXAsync() 这种 异步非阻塞吗? |
24
GM 2021-04-20 16:39:58 +08:00
@balabalaguguji 对员工要求高了不少,这也是我认为会很难推广起来的原因。
|
25
Jirajine 2021-04-20 16:43:03 +08:00 via Android 3
先分清楚异步 /同步 io 、异步 /同步编程范式、async/await+future 模型、抢占式 /协作式多任务调度模型等概念之间的联系和区别。
推荐阅读这篇 https://os.phil-opp.com/async-await/ 只看前面与语言无关的部分就可以了。 |
26
hronro 2021-04-20 16:46:53 +08:00
异步可不一定是单线程的,也有 m:n 的多线程的实现,比如 Go 。所以你说的一个地方阻塞了,整个服务全部卡死,这种情况只能说某些异步的实现确实这样(比如 Node.js ),但你不能说异步都是这样的。
另外有些人说的异步比多线程心智负担大,我不太认同。可能有很多人对多线程的认知远远超过异步的,所以对他们来说异步确实比起多线程要难理解一些,但如果是对异步和多线程两个概念都不怎么了解的新手来说,我个人觉得异步还是比多现成要多线程的心智负担要小一些的。 |
27
balabalaguguji OP @shuax #22 你来一个吧。其实我的重点是说异步需要注意的点,不小心引起异步跟多线程的比较战。
|
28
balabalaguguji OP @hronro #26 嗯,我对 go 还没了解过。确实可能每个人对线程和异步的熟悉度不一样,可以忽略两个的比较吧,重点是告诉大家异步需要注意的点。
|
29
keepeye 2021-04-20 16:52:03 +08:00 2
对人员要求高?能高到哪里去啊... 这都无法把控还是不要招进来的好
|
30
QiShine 2021-04-20 16:54:55 +08:00
异步阻塞其实更符合人对世界的直觉,比同步更容易理解
|
31
Jirajine 2021-04-20 16:57:42 +08:00 via Android
另外再补充一个所谓“多线程模式”(即抢占式调度)在某些方面反而可能有更大的心智负担
https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ 协程模式在这个问题上要稍微好一点。 |
33
TypeError 2021-04-20 17:12:06 +08:00
了解的太少
python 异步你都没了解全 |
34
balabalaguguji OP @TypeError #33 可以指点下哪里理解不到位,虚心学习
|
35
balabalaguguji OP @TypeError #33 玩过 tornado,后面找异步库太麻烦放弃了。
|
36
seakingii 2021-04-20 17:32:34 +08:00
作者你这水平,还是不要随便发现什么结论比较好
|
37
balabalaguguji OP @seakingii #36 有哪里说得不对你可以指出来
|
38
passerbytiny 2021-04-20 17:40:24 +08:00 via Android
没事不要下大结论,你这异步,是特定语言的异步组件,而不是异步编程。“异步只有一个线程”,可是吓死我这个 Java 开发了。
综合楼主和楼上的回复,楼主说得异步,应该是单“主”线程异步框架,这些框架应该是托胎与 GUI 框架上的异步模型。如果你做过.NET 应用开发,你就会发现,主要用在 GUI (或交互部分)的异步,和主要用在服务部分的多线程,是要同时用的——二者压根就不是对立的。 |
39
aper 2021-04-20 17:40:51 +08:00
单线程异步最大缺点是中间不能穿插任何阻塞 IO 操作,比如请求 DB,rtt 是 1ms,QPS 最高也就到 1000,还不包括其他业务逻辑
|
40
tairan2006 2021-04-20 17:42:55 +08:00
建议楼主发 python 节点…
|
41
balabalaguguji OP @passerbytiny #38 嗯,其他语言我确实不了解,这里可以理解为我说的是 nodejs 做 web 服务端
|
42
balabalaguguji OP @tairan2006 #40 nodejs 也是
|
43
balabalaguguji OP @aper #39 跟我表达的其实是一个意思吧
|
44
TypeError 2021-04-20 17:46:38 +08:00
|
45
aper 2021-04-20 17:47:43 +08:00
@balabalaguguji 要求没有多线程同步高,只是后面都是 callback hell,贼恶心。而且不能有很重的 task,要不然很容易挂
|
46
balabalaguguji OP @TypeError #44 感谢指点,我确实没了解到
|
47
Wincer 2021-04-20 17:53:26 +08:00
Python 即使采用同步框架( Flask 等),在视图函数内也可以使用异步操作(请求多个数据库之类的),我司就是这么做的,不过会有一些坑。所以这俩也不是对立面,只不过现在大多数人把异步当成多线程的替代品了。
|
48
libook 2021-04-20 18:02:06 +08:00 1
异步是个流程上的通用概念,不同技术栈的异步实现可能会有区别。
个人认为,异步最大的优势是可以让机器一直干活,不会因为一个未返回的结果而干等; 异步的缺点就是运行时机不确定,两个同时执行的异步过程中的操作不一定谁先谁后,所以如果多个异步过程对同一存储空间执行写操作的的话,每次读这个空间可能得到的值会不一样,多数需求场景下这就是个脏数据。 所以为了避免运行时机的不确定所带来的预期之外的行为,我们有可能得把外层很多原本就可以正常工作的同步代码改写成异步代码,最坏的情况就是要把整个项目所有的代码都写成异步代码。这算是异步间接带来的成本吧,不过一些语言在这方面做了些优化尽量降低开发者的头痛程度,使得不需要增加多少开发成本就可以享受异步红利。 一些需求场景中,工作可以拆分成几部分,而每项工作之间没有严格的依赖关系,在这些场景中用异步就是非常合适的,比如多数互联网应用。 但每项工作之间有严格的依赖关系的话,就不能用异步了,比如算斐波那契数列,每一次计算都依赖上一次计算的结果。 异步与多线 /进程是两个维度上的概念,同步异步是流程概念,线 /进程是操作系统资源调度上的概念(这个和多核、超线程等硬件技术也不是一个维度上的概念),比如我向操作系统申请了 2 个线 /进程,也是可以让线 /进程 1 先执行,执行完返回结果后再让线 /进程 2 执行,这个也可以称作为同步过程。当然通常我们开多个线 /进程就是为了让它们能够同时运行,以成倍地提高计算效率,所以可以说我们大多数用线 /进程的场景都是会采用异步实现的。 没有银弹,不同技术有不同的特性,架构选型的实质就是找最适合当前需求的技术。 |
49
Muninn 2021-04-20 18:03:05 +08:00
从 python 转投 golang 之后再也没有这些烦恼了……
|
50
cyrbuzz 2021-04-20 18:06:07 +08:00 2
个人理解= =。
异步模式不是把同步的代码异步化,而是那段代码本身就是一个异步的 IO 。 想把一段代码异步化,得先知道这段代码能不能异步,比如 ``` a += 1 ``` 是一段不能异步的代码。 而一个网络请求则一般都会提供 阻塞和非阻塞 两种模式: ``` http({ async: true(false) }) ``` 这样的配合事件循环就可以做到异步。 在 Python 中,像是`requests`是一个阻塞的网络请求,它并没有提供非阻塞的参数,无法直接用类似线程的`async(requests.get, xxxx)`这样的方式去将它异步化,而是应该在底层 socket 建立连接时就使用非阻塞的模式,交由异步的事件循环在这条 socket 等待响应的这段时间去干别的事。 按楼主的举例,commands 是一个阻塞的命令行,确实想用异步应该寻找的是一个非阻塞的命令行而不是想着有一个函数可以把 commands 异步化(事实上 Python 也有,`asyncio.base_event.run_in_executor`函数可以让传入的同步函数放在一个线程 /进程池中执行,同时外部还可以继续用异步的 async/await 或者回调)。 而在 Js 中,如果你在主线程中执行一段一直会占用 CPU 的代码(while(1){})也会阻塞住整个页面。 异步主要是用来解决 I/O 密集的问题的,I/O 所花的时间主要在网络读写,硬盘读写等。 如果一段代码的主要消耗的是 CPU,用异步并不是一个好选择而是应该尽可能榨干 CPU 的性能,衍生出来的就是线程(协程)/进程。 所以在 Python 这个啥程都有的语言里... 在主要是 I/O 的场景里,用非阻塞的库上异步绝对是一个性价比高的选项,其他程切换的开销再小也有开销。 在主要是计算的场景里,多跑几个 Python 进程比啥都管用。 访问量不大的网站用什么根本无所谓...数据量够小的情况下,选择排序和快排根本看不出差别。 |
51
balabalaguguji OP @libook #48 感谢科普,学习了。
|
52
balabalaguguji OP @Muninn #49 我也是 python,学习了下 nodejs,感觉想转,异步写起来爽很多。听你这么说,golang 更爽。
|
53
rahuahua 2021-04-20 18:16:02 +08:00
多线程异步不香吗?
|
54
balabalaguguji OP @cyrbuzz #50 感谢科普,个人觉得 python 写异步太麻烦了,nodejs 方便好多。
|
55
balabalaguguji OP @rahuahua #53 两个结合是挺好,还没了解过具体怎么做,不过按我的了解 nodejs 是只有一个主线程的,没法创建线程。
|
56
balabalaguguji OP @Muninn #49 可以科普下 golang 怎么个爽吗
|
57
rahuahua 2021-04-20 18:22:22 +08:00
@balabalaguguji 网络并发这块没有比 Go 更香的了。完全不用考虑同步异步,所有的网络 I/O 都是同步代码、异步执行,async/await 都不用写
|
58
shyling 2021-04-20 18:26:18 +08:00
node, go 都是从一开始都支持。。。相比 python 从同步到异步,就少了很多坑
|
59
rahuahua 2021-04-20 18:27:14 +08:00
@balabalaguguji 某位大佬(好像就是 node 之父)说过跟 Go 相比,NodeJS 并发就是个玩具 😃
|
60
crclz 2021-04-20 18:27:44 +08:00
“异步现在支持最好的应该是 nodejs 吧”
nodejs 对异步的支持的确挺好,但别忘了 async/await 的爹是谁——C#。C#对于异步的支持一如既往的走在编程语言的最前列。 --- “所以你得时刻记得,如果会阻塞的方法,就得用异步的库” C#也存在误用阻塞代码会让性能大幅下降的问题,但是这个问题可以从包管理器(nuget)安装检测这种错误的(代码提示)包来终结此类缺陷。 --- “如果你的网站不是特别大的访问量,可以使用多线程模型,够简单;如果是需要高并发,有大量用户,可以用异步框架并始终记住不要用阻塞方法” 据我所知,C#的 Asp.NetCore 可以无缝地切换。Controller 可以拥有同步和异步的方法,不冲突。另外,如果某个年代久远的库只提供同步调用,我们只需讲 Controller 的这个方法写成同步方法即可。 (整体上来说,对于 java 这类编程语言,从简化开发、减少 bug 上来说,楼主的观点是没有错的) |
61
balabalaguguji OP @rahuahua #59 竟然是这样,突然对 go 很有好感
|
62
rahuahua 2021-04-20 18:29:08 +08:00
@balabalaguguji Go 就是多线程加 I/O 非阻塞执行,强大的并发调度 Runtime,语法简单,看不出异步的痕迹,全在 Runtime 里面。
|
63
superrichman 2021-04-20 18:32:14 +08:00 via iPhone
就是一句话 “一处异步,处处异步”
|
64
zhuichen 2021-04-20 18:32:20 +08:00
所以异步的优点有哪些?
|
65
balabalaguguji OP @crclz #60 感谢指点,C#没想到走在异步的最前列,没用过这个语言。嗯,我讲的是基于 python 和 nodejs 的了解。
|
66
balabalaguguji OP @zhuichen #64 网上都出都在说优点,随便一早就一堆,不过我说的这个缺点,没几个人说。
|
67
balabalaguguji OP @superrichman #63 是的。某天想把我的 python 代码改为异步的,发现好多东西要换,太麻烦放弃了。
|
68
balabalaguguji OP @rahuahua #62 感觉很不错,完美结合两个东西
|
69
momocraft 2021-04-20 18:35:32 +08:00
py 这样后天改出 async io 的, 是不是不匹配的现有代码会多一些
另外事件循环能写出死锁的人 换了多线程未必写不出 |
70
balabalaguguji OP @momocraft #69 常见的就是递归调用容易死循环。多线程写出来没关系,不会卡住整个网站,这才是重点。
|
71
lewinlan 2021-04-20 18:41:14 +08:00 via Android
看标题就猜到又是一个被 python 毒害不浅的
|
72
balabalaguguji OP @lewinlan #71 是的,某天转异步时发现很蛋疼,后面学习了下 nodejs,发现好多了。
|
73
carity 2021-04-20 18:47:55 +08:00
"异步因为是只有一个线程,如果有一个地方阻塞了,那整个网站全部都卡住了"
看到这就懵逼了,nodejs 我不熟悉,然后看到 python 我懂了 :doge |
74
SenLief 2021-04-20 18:53:10 +08:00 via Android
python 又不是一个新的语言,没什么历史包袱,不能站在结果的角度去看过程。选择了这个技术栈也同样选择了它的优缺点。当然如果有能力建议帮她改进下,让更多人更好用。
|
75
zwy100e72 2021-04-20 19:18:48 +08:00 1
感谢楼主、参与的人没有把技术问题升级到互相攻击的地步,这样的讨论氛围实在太难得了。
|
76
luoqeng 2021-04-20 19:20:28 +08:00
建议学学 golang,语言级别的协程支持,和你平时写同步代码没什么差别。
|
77
jones2000 2021-04-20 19:22:39 +08:00
异步不好用, 很多逻辑都被打断了,下个数据就异步, 同步的逻辑多好处理,简单。
|
79
xiubin 2021-04-20 19:43:48 +08:00
『异步因为是只有一个线程,如果有一个地方阻塞了,那整个网站全部都卡住了』
不用异步就不会卡住吗?没 get 到楼主的意思是什么。还有异步和死循环有什么关系呢 |
80
jeffwcx 2021-04-20 19:45:58 +08:00
node 底层的 epoll 还是多线程啊,可以说 node 的异步是封装地很好的多线程
|
81
zhanlanhuizhang 2021-04-20 19:52:20 +08:00
异步:就是新建一个线程处理(就是多线程)。怎么会把网站卡住。除非你用了同步。
|
82
ReferenceE 2021-04-20 19:58:33 +08:00 via Android
一处异步,全部异步
习惯就行问题不大 不过不写 Python,TAP 模型挺顺手 |
83
xuanbg 2021-04-20 20:43:32 +08:00
单线程的情况下,无论异步还是同步,一处阻塞就全卡住。这是单线程的问题,不是异步的问题。
|
84
helloworld000 2021-04-20 20:49:51 +08:00
看了快 100 楼了,没人说 Twisted ?
虽然 py async 确实不怎么样,但是 Twisted 还是要提的 |
85
Muninn 2021-04-20 20:55:45 +08:00
@balabalaguguji go 学起来没有比 python 难太多。解释起来就太复杂了,直接学起来试着写写就懂了。 推荐这篇文章吧: https://tonybai.com/2017/04/20/go-coding-in-go-way/
|
86
billlee 2021-04-20 21:04:10 +08:00
java 不就是典型的多线程异步
|
87
iseki 2021-04-20 21:10:54 +08:00 via Android 1
同步阻塞模型下往往会开一堆线程,这其中的同步并发安全问题也会带来极大的心智负担。纵然异步非阻塞模型下往往也都不是单线程的,但心智负担远没有这么大,很多时候核心部分处理正确就没问题了
|
88
liuhan907 2021-04-20 21:32:10 +08:00 via Android 1
你这标题应该改叫做 “在一堆技术菜的抠脚程序员维护的老项目里引入单线程异步的缺点” 才贴切。不然你让 go/c#/kt/rust 等等等一大票良好支持多线程异步的语言脸往哪搁啊。连 C++都有多线程协程了。
|
89
hejingyuan199 2021-04-20 21:38:46 +08:00
看楼主发言,我感觉我理解错了同步 /异步、IO 阻塞 /非阻塞。
看下面回复,我发现好像楼主表达不是很清晰。不过楼主知道自己在说什么。 “异步因为只有一个线程”这句话给我整懵了。 都异步了,为什么才一个线程?都异步了,说明两件事情同时进行了,那应该在两个线程上或者两个进程上吧。只不过,如果是单核,那么需要分时处理。如果是多核,那就是真正的同时进行,一个核上跑一个。 后面我就更看不懂了。 如果我有理解错误,请原谅,只是基于自己的理解参与一下讨论。 |
90
ipwx 2021-04-20 21:48:16 +08:00
我个人觉得多线程更难搞。。。
|
91
akira 2021-04-20 21:57:53 +08:00
同步的优点 = 异步的缺点
异步的优点 = 同步的缺点 应该是这样 没错吧 |
92
lujjjh 2021-04-20 22:13:47 +08:00
在写 GUI app 的时候都会注意不要 block 主线程,不然会导致 event loop block 住,整个 UI 就卡死了。你说的其实也是这个意思。
node.js 和 Python 里基于绿色线程的各种框架,event handler 不会跑在单独的线程 / 进程里,所以一旦业务函数 block 住,其他请求就进不来了。 node.js 相对 Python + gevent 来说心智负担小很多,对于会 block 住 I/O 的函数,标准库里基本会标明 xxxSync 。然而并没有办法阻止其他人或者直接 /间接依赖使用 xxxSync,除非 review 包括依赖在内的所有代码。所以我倾向于这个问题是没法完全避免的。 那怎么办?其实不必过于担心这个问题,一般写业务,能产生 I/O 的地方也就那么几种类型,选择好对应的库就可以了。对于其他场景,比如写基础设施(写基础设施真的会用 Python 么?),code review 能杜绝大多数问题,主要看能产生 I/O 的地方。剩下的就交给压测了,这种明显会产生性能瓶颈的地方绝对是可以压出问题来的。 |
93
alvins 2021-04-20 22:32:55 +08:00
不能基于语言说基本概念,只是你恰好碰到的是不那么完美的
|
94
NilChan 2021-04-21 02:49:27 +08:00
异步同步和线程是一个概念吗?
|
95
domodomo 2021-04-21 06:33:09 +08:00
异步最大的缺点就是对设计和编写的要求比较高,一不小心很容易把整个项目搞砸。
|
97
balabalaguguji OP @carity #73 很多人都不知道这个情况的,以为可以随便写代码,实际上异步只有 IO 是异步,其他逻辑都是同步的,一卡就所有都卡死了
|
98
balabalaguguji OP @zwy100e72 #75 嗯,也有人攻击,不过我虚心跟他们请教,所以没吵起来,毕竟每个人的知识面不一样,用的语言不一样,理解也会不一样
|
99
balabalaguguji OP @luoqeng #76 嗯,对 golang 产生了很大的兴趣
|
100
balabalaguguji OP @jones2000 #77 是的,就像我说的,如果不是有很大的性能要求,简简单单的用多线程框架简单很多。
|