Eventloop 多线程版本优势在哪 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ojh
V2EX    问与答

Eventloop 多线程版本优势在哪

  ojh 2022-06-24 10:20:14 +08:00 1630 次点击
这是一个创建于 1210 天前的主题,其中的信息可能已经有所发展或是发生改变。

之前看书说单线程 Eventloop 处理事件时候可能因执行时间过长把线程阻塞了,所以演变成每个事件处理都用线程池里的线程去处理。然后我发现这种演变跟直接基于线程池的每个请求对应一个线程的方式相似,前者优势仅仅比后者节省了读和写的等待。我想问除了我认为的优势外还有其他优势吗

6 条回复    2022-06-26 14:06:09 +08:00
qrobot
    1
qrobot  
   2022-06-24 10:37:39 +08:00
事件循环和线程池最大区别在于对线程的利用率。

采用多线程的方式去解决效率的问题,就存在以下几个问题。

1. 共享数据、同步、通信
2. 并发 /锁
3. 线程生命周期的开销高。如线程的创建和销毁,需要操作系统辅助
4. 资源消耗。大量如空闲的线程会占用内存,大量线程竞争 CPU 时会有额外开销
5. 稳定性。通常操作系统限制了一些资源,如最大线程数,线程的栈大小等。过多线程会可能会出错


而事件循环的方式可以最大的利用单线程,并且以最小的成本解决以上的问题。

你看一下 Actor mode 就知道 Event loop 是非常优雅的。
ipwx
    2
ipwx  
   2022-06-24 10:40:43 +08:00
其实吧,业务代码没那么多 CPU 时间很长的片段的。一般都是直接写一点数据处理,await 数据库 /文件 IO 。

好处实在太大了。因为在线程池模式下,无论是数据库还是文件 IO 都会阻塞一个线程,也就是说就算你业务逻辑再简单,也会耗尽线程而无法提高并发。现在直接 async ,阻碍你并发的只有后端数据库和网络的速度。网络速度可以随意 100MB/s ,数据库可以集群(提供更快的读、更慢的写),这样就非常 nice 。
littlefishcc
    3
littlefishcc  
   2022-06-24 10:45:15 +08:00
> 之前看书说单线程 Eventloop 处理事件时候可能因执行时间过长把线程阻塞了,所以演变成每个事件处理都用线程池里的线程去处理。

你这句话根本不成立,多线程的 eventloop 执行也不应该长时间跑,如果长时间要跑就单独丢到其他线程队列任务中去,做完再回调回去即可。eventloop 本质用来推动事件流动,而不是在里面跑很长的业务。不然你的程序业务大了,你怎么保证稳定性。
演变线程池只是业务事件太多,提高 event 处理能力,同时利用多 CPU 资源。。
ipwx
    4
ipwx  
   2022-06-24 10:49:54 +08:00
... 然后书上说的不错,但是没有告诉你的是什么叫“时间太长阻塞”。

实际上你看到的 “每个事件处理都用线程池里的线程去处理” 太泛泛了。具体而言,一般我们都是把 CPU 时间开销大的逻辑丢进线程池然后 await 的。比如算一个 PageRank 分数之类的(笑)。

上面的仁兄提到了 Actor model 。我很感动居然还有人知道这玩意儿,但实际上这玩意儿写普通的业务逻辑不如 async await 高效。举个例子,如果你要读数据库,在多线程 Actor model 下你要怎么做?难不成直接同步阻塞等数据库结果?那不好意思,在大部分语言( C++/Java/Python )中,你又阻塞了一个线程池。纯粹的 Actor model 要求你业务代码把数据库请求 send 给 DBActor ,DBActor 应该是一个负载均衡,后面接着一串多线程的 DBActors 。然后 DBActors 阻塞读写,出来结果再 reply 到业务 Actor 。总之纯粹的 Actor model 写业务代码挺麻烦的。

但是 Actor model 上限很高的,是屠龙刀。它能保证每个 actor 同时只有一个线程在跑,直接保证所有数据的线程安全。这种屠龙刀就不该用来写普通的业务代码。
----

综上所述其实 async / await 写业务代码其实很舒服的,在大部分语言中你不会喜欢其他模式的。

除了 Go 语言。

Go 语言是个工程妥协性非常强的语言,为了解决普通程序员不那么高明这一“人力 BUG”,它做了一些在我看来很丑陋的(但是确实能解决不那么博闻强识的程序员写出来的代码问题)选择。比如它会自动把同步的等待代码直接切出来控制权,强行在 Go vm 层面自动地把同步代码给打断等待。相当于你写了同步等待的网络 /文件 IO ,它自动翻译成 async await 了。这个确实,不得不承认,虽然在我看起来很丑陋

这意味着程序员并不能 100% 控制 Go 语言的实现,你写个比如 PageRank 的算法可能比不上 C++,因为有一堆你不知道它会干嘛的额外动作。

当然,我这么评价 Go 语言,是因为我从线程池、Event loop ( select epoll )、Actor model ( Akka 或者我自己 C++ 手撸的)、callback 之类的东西我都用过。C++、Python 、Scala 、JS 我都写过不少项目。嘛,没那么博闻强识的程序员确实 Go 语言包办更舒服。
ipwx
    5
ipwx  
   2022-06-24 10:59:31 +08:00
总之如果只看名词不看实际(实现方式),各种语言、框架的并发就是大坑,不同概念层出不穷,会让楼主混乱的。

如果楼主不想继续纠结这么多乱七八糟的概念,建议转 Go 语言。那边的 Goroutine 中的同步代码 = Event Loop + 协程(相当于 Python/JS 手动 async/await ) + 线程池。实际上完全解决了楼主你书里面看到的问题,但是其实你书上的概念一个都套不上去,因为它直接从 vm 层面给你扬了这些概念(笑

虽然你像我这么分析还是能对应到你书上概念的
ojh
    6
ojh  
OP
   2022-06-26 14:06:09 +08:00
@qrobot 你没看清我的问题,单线程的 Eventloop 为啥要演变成多线程的 Eventloop
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5429 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 25ms UTC 07:55 PVG 15:55 LAX 00:55 JFK 03:55
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86