V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
ojh
V2EX  ›  问与答

Eventloop 多线程版本优势在哪

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

    之前看书说单线程 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   ·   实用小工具   ·   3265 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 12:39 · PVG 20:39 · LAX 04:39 · JFK 07:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.