最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Node.js eventloop + 线程池源码分析(建议精读)

    正文概述 掘金(涂鸦大前端)   2021-04-18   785

    线程池

    线程池是个啥

    先开启第一个问题,何为线程池。 一项技术的诞生往往是为了解决某个问题,那线程池也不例外。

    我们先来假设一个场景。

    假如你在某纪佳缘网上班,你的老板让你开发一个推荐服务,做的事情很简单,当有用户访问你的服务的时候,你需要根据用户的一些特性(比如性别)给他推荐对象。比如怪怪在访问的时候,你就需要推荐 富婆

    Node.js eventloop + 线程池源码分析(建议精读)

    当你接到这个任务,首先要想到的就是每个用户过来的请求应该是个 独立的线程。因为 用户和用户之间是隔离的,而每个线程做的事情就是一系列推荐操作。

    当然,这难不倒优秀的你,你可以在每个用户请求的时候新建一个线程,在请求响应结束后销毁它,一切都很美好。

    燃鹅,因为你推荐的太到位了,你们 网站火了,一群富婆争着来注册。这个时候问题也就来了,如果 1000 个富婆同时点击,那意味着 1000 个请求会过来,你是不是就要建立 1000 个线程呢?

    在这一系列请求结束后,这些线程是不是又会被销毁?线程创建最直观的开销就是 内存,这样的频繁创建和销毁对性能的影响显而易见,同时这样的设计并不能撑其瞬时 峰值流量

    Node.js eventloop + 线程池源码分析(建议精读)

    因为这样的设计,富婆们得不到满足,某纪佳缘岌岌可危。

    这时,线程池应运而生。之前讲过,一项技术的诞生永远是为了解决某些问题,那线程池解决了什么问题呢?总结下其实就是线程的生命周期管控。下面我们来细细分析。

    线程生命周期管控

    先看我们的问题,频繁的创建和销毁线程。解决的办法是啥呢?必然是 线程复用

    一个线程被创建之后,即使这一次响应结束了,也不让他被回收,下一次请求来的时候依然让他去处理。

    这里很关键的点在于如何让一个 线程不被回收

    看似很神奇,一个线程执行完了操作还能继续存在?这么持久?做法其实很简单,写一个 死循环 即可。线程一直处于循环中,当有请求来的时候处理请求,当没有的时候就一直等待,等到了再执行处理,处理完再等待,反复横跳,无限循环。

    Node.js eventloop + 线程池源码分析(建议精读)

    这里引申出了第二个关键点,处于 死循环中的线程怎么知道啥时候有请求要给他处理

    这里不同语言实现方式不完全相同,但大同小异,本质上一定是基于阻塞唤醒。当没有任务的时候,所有线程处于阻塞状态,当任务来的时候,空闲线程去竞争这个任务,取到的线程开始执行,未取到的继续阻塞。

    这里大家可能网上看过行行色色的线程池解读,但要深刻理解的话,还是要 从源码入手。(在源码面前,一切的花里胡哨都苍白无力)

    Node.js eventloop + 线程池源码分析(建议精读)

    先给出 libuv threadPool 源码 地址: 源码地址

    我们直接截取线程池实现的核心部分,如下图里的注释,基本实现符合预期。

    Node.js eventloop + 线程池源码分析(建议精读)线程池源码1Node.js eventloop + 线程池源码分析(建议精读)线程池源码2

    简单说明下,上面代码里 uv_cond_signal 等同于唤醒阻塞线程,uv_cond_wait 等同于让当前线程进入阻塞状态。

    线程池总结

    最后总结下就是,线程池利用死循环让线程无法结束,在等待任务期间处于阻塞状态,利用阻塞唤醒来让线程接收任务(本质上阻塞唤醒基于信号量),从而达到线程复用,结束当前任务后进入下一次循环,周而复始。

    eventloop

    eventloop 是个啥

    eventloop 的含义如同其名字一样:事件循环。

    说的通俗一点其实就是一个 while(true)循环,循环里面做的事情就是不断的 check 有没有待处理的任务,如果有就处理任务,如果没有就继续下一次循环。

    大致流程如下图。

    Node.js eventloop + 线程池源码分析(建议精读)

    eventloop 的思想很简单,他并不关心你的回调如何实现,IO 操作何时结束。

    他做的事情就是不断的去取事件,取到了就执行。那这里有一个关键的点就是他在哪里取的 event 呢?

    答案是: watcher。每个事件循环中都会有观察者,每轮循环都会去观察者中拿事件,然后执行。其实这个所谓的 watcher 就是一个用来存放事件的 queue(队列)。

    怎么来理解这个 watcher 呢,深入浅出 nodejs 里面给了一个很形象的比喻。


    在餐厅里,前台小妹往往负责记录客人的点菜,厨师在后厨做菜。小妹在拿到客人的菜单后会把菜单放到厨房,而厨师只需要不断的看菜单,做菜,再看菜单,做菜。他并不关心是谁点的菜,也不关心这个菜在什么时候点的。

    这里这个放菜单的地方就是那个 watcher,本质上是一个 queue,厨师就如同 eventloop,不断的处于做菜的循环中,每一轮循环会去取 queue 里面的请求,如果有回调就执行回调,没有的话进入下一轮循环。

    Node.js eventloop + 线程池源码分析(建议精读)

    eventloop + 线程池 = 异步非阻塞

    上面比较详细的讲解了线程池和 eventpoll,接下来我们来看一下如何用其来实现异步非阻塞。

    我们来一步一步捋清思路。首先,可爱的你发起了一个 IO 调用,从 《大前端进阶 Node.js》系列 异步非阻塞 中讲过,一个 IO 调用要么是阻塞调用,要么是先非阻塞的发起 IO,再在需要看结果的时候阻塞的去获取,显然这两种模式都不是我们想要的。

    我们要的是异步非阻塞,所以这个 IO 调用一定不是在主线程中执行,这个时候我们就能联想到上面的线程池。

    主线程不能被阻塞,但线程池里面的线程可以,主线程只需要把 IO 调用交给线程池来执行,自己就可以愉快的玩耍,以此达到了我们的第一个目标: 非阻塞

    异步 呢?如何让线程池里面的调用在结束的时候去执行回调?这个时候 eventloop 闪亮登场。

    Node.js eventloop + 线程池源码分析(建议精读)

    在线程池 IO 处理结束后,会主动的把结束的请求放入 eventloop 的观察者(watcher)中,也就是我们的 queue 中,eventloop 处于不断循环的状态,当下一次循环 check 到 queue 里有请求的时候,就会取出来然后执行回调,这样我们想要的 异步 就达到了。

    最终通过线程池和 eventloop 结合,呈现出的效果就是,当你发起一次 IO 调用,你无需阻塞的等待 IO 结束,也无需在想利用 IO 结果的时候不断的轮询,整个 IO 过程 对主线程而言非阻塞,并且自动结束时执行回调,达到我们想要的异步非阻塞。

    最后,我们引用一张《深入浅出 Nodejs》里的图。

    Node.js eventloop + 线程池源码分析(建议精读)异步非阻塞

    如上图,在发起异步调用后,会封装一个请求参数,里面会包括参数和结束时要执行的回调。

    这个 request(请求参数) 封装好后会扔给线程池执行,线程池里面的线程如果有空闲,就会在线程池的 queue 中去取这个 request 并执行 IO 操作。

    在执行结束之后通知 IOCP,其实就是把这个 requeat 放入一个 queue,这个队列就是线程池和事件循环之间的枢纽。

    事件循环在循环的时候发现队列里面有请求,就会取出来并执行相应的回调,一次完美的异步非阻塞就此完成。

    总结

    仔细的看完怪怪的 Node 异步非阻塞(上)(下)两个系列,还不能吊打面试题你尽管来找我~

    libuv threadPool 源码 都带你看过了,还不明白,就真的说不过去了!!

    最后,精辟的完美总结如下。


    核心总结:Node 利用线程池来执行 IO 调用,避免阻塞主线程,执行结束后把结果和请求放入一个队列,利用事件循环来取出队列的请求,最后执行回调,达到了异步非阻塞的效果。

    Node.js eventloop + 线程池源码分析(建议精读)

    作者更多原创热文传送门,biubiu~:

    • 《吐血整理》系列 大厂前端组件库工具集合(PC 端、移动端、JS、CSS 等)
    • 《大前端进阶 Node.js》系列 多进程模型底层实现(字节跳动被问)
    • 《大前端进阶 Node.js》系列 双十一秒杀系统
    • 《大前端进阶 Node.js》系列 异步非阻塞(上)

    喜欢的小伙伴加个关注,点个赞哦,感恩??


    下载网 » Node.js eventloop + 线程池源码分析(建议精读)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元