循环水泵模型(浏览器 EventLoop 事件循环并发模型)

Posted

篇首语:行为决定性格,性格决定命运。本文由小常识网(cha138.com)小编为大家整理,主要介绍了循环水泵模型(浏览器 EventLoop 事件循环并发模型)相关的知识,希望对你有一定的参考价值。

循环水泵模型(浏览器 EventLoop 事件循环并发模型)

前置概念

在了解 EventLoop 事件循环 前,先铺垫一些基础概念。

堆和栈

堆和栈是计算机领域的术语:

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。

堆(heap)是计算机科学中一类特殊的数据结构的统称。

简单说(不严谨),我们程序中执行 function 后,这些函数将以 栈帧(Stack Frame) 的形式推到栈内,这些 栈帧 将根据先进先出的方式按序执行。 执行过程中,涉及到的引用变量将放置到堆内存中。

进程和线程

进程和线程在计算机领域术语,简单看下他们描述:

线程(thread):是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程(Process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

有一个例子比喻的很好: 进程 好比一个工厂,它有独立资源。多个工厂之间 相互独立线程 是工厂中的工人,多个工人协作完成任务。 工厂内有一个或多个工人 ,工人之间 共享工厂内的资源

浏览器是多进程的!

打开电脑任务管理器,我们能看到当前正在运行的 程序进程

多个相同的进程会归到一类中,比如我在 Chrome 浏览器打开了多个 tab 页面,此时总进程数达到了 35 个。

此时会有个疑问,为何 tab 数量和浏览器进程数量不一致?可以打开更多工具/任务管理器查看浏览器运行的具体内容:

能看到有 GPU,浏览器基础功能,扩展程序进程,另外每个 tab 页对应的进程外,其他与其相关的 Service Worker 及辅助框架线程。 详细关于浏览器进程的说明,可以看在浏览器输入 URL 后发生了什么?js 为何是单线程的?

首先如果知道浏览器内部的进程和线程的运作模式,那就容易理解为何 js 是单线程: 简单说,我们打开页面后, 浏览器进程 就开始工作,并拉取对应前端资源。拉到资源后,移交给 渲染进程 做页面渲染。 渲染进程 会开设一条 主线程 用来做 DOM 的解析,计算样式,以及构建出 Layout树,Layer树... 虽然 主线程 已经做了那么多工作,但它还要运行我们的 js 代码 。因为这个 主线程 被设计为 线程 ,并且单个 渲染进程 中只存在一个 主线程 ,所以跑在里面的 js 也只能是单线程级的,环境如何所以何谈多线程,或者多进程。 从另一方面理解,如果同一段代码由多个 js 线程在运行,必然存在并发问题,涉及到 DOM 的修改的话,浏览器也不知道怎么处理渲染关系。

EventLoop 事件循环并发模型

函数和栈的运行方式

这是 MDN 上有关 js 运行时的模型概念图:

它分为3个部分:存放栈帧的栈队列(Stack),存放对象的堆内存(Heap),以及放置待处理的消息队列 (Message Queue)。 了解大概组成部分后,下面通过一段代码方法解释栈和堆:

function foo(b)   let a = 10;  return a + b + 11;function bar(x)   let y = 3;  return foo(x * y);console.log(bar(7)); // 返回 42

MDN 对其详细说明:

当调用 bar 时,第一个帧被创建并压入栈中,帧中包含了 bar 的参数和局部变量。当 bar 调用 foo 时,第二个帧被创建并被压入栈中,放在第一个帧之上,帧中包含 foo 的参数和局部变量。当 foo 执行完毕然后返回时,第二个帧就被弹出栈(剩下 bar 函数的调用帧)。当 bar 也执行完毕然后返回时,第一个帧也被弹出,栈就被清空了。

每调用一次方法,该方法及参数将以 栈帧 的形式推到 栈内 ,相关引用对象变量则会放到 堆内存 中。下图是帧栈的形成过程:

消息队列和EventLoop

注意到还有个 消息队列 Queue 没有讲到,在 script 代码中,根据不同的 Web APIs(比如:点击事件,定时任务,网络请求等)分为不同的任务:宏任务 macroTasks 和微任务 mircoTasks。任务会以 回调函数 Callback 按“一定的规则”放到消息队列中。 EventLoop 是一个自始至终运行的机制,它会等待消息队列是否还有任务加入,如果没有,会按“一定规则”运行消息队列中的回调函数。其伪代码如下:

// 伪代码while (queue.waitForMessage())  //等待待处理消息  queue.processNextMessage(); //执行消息

整个 EventLoop 的运行过程如下:

宏任务与微任务

这是一张主进程内描述了 微任务队列事件队列 的概览图:

宏任务包括: script代码,setTimeout,setImmediate,I/O 等。 微任务包括:Promise,MutationObserver,process.nextTick(nodejs)等。 相信一定看过类似的题目,试着说下打印顺序?

console.log('script start');setTimeout(function ()   console.log('setTimeout');, 0);Promise.resolve()  .then(function ()     console.log('promise1');  )  .then(function ()     console.log('promise2'););console.log('script end');// script start// promise1,promise2// setTimeout// script end

首先 script 会以宏任务身份运行,会将调用代码推到栈内,秉着先进先出的规则,先输出 script start。 紧接着遇到 setTimeout 和 Promise,根据 Web APIs 分别归类到 宏任务 Event 队列微任务的 microtask queue (PromiseJobs)队列,因为第一个宏任务没有结束, EventLoop 并不会开始执行消息队列的回调函数(只有在 JavaScript 引擎中没有其它任务在运行时,才开始执行任务队列中的任务),最后运行到末尾的代码,输出 script end,当前宏任务正式结束。 接下来 EventLoop 会从消息队列依次把回调函数拿来执行,由于第一个 setTimeout 的定时即使为 0 但它还是没有推到消息队列,微任务则会优先于它,输出了 promise1,promise2,当微任务全部执行完后,等到下次 EventlLoop 的循环才输出了 setTimeout。

非阻塞式I/O

知道了 EventLoop 的运行过程后,就能理解为什么 js 是一种 非阻塞式I/O 的模型。 符合特定规则的 Web APIs 会专门放置到一个任务队列,依靠 EventLoop 机制,在主任务代码运行完后,不停轮询调用这些任务队列中的回调方法。这样我们的 js 代码不会是同步式,而是各种 Callback 回调风格的事件驱动式。 由于 js 依旧是单线程,如果我们主线程上的代码逻辑比较“吃”性能,就会造成一定的卡顿。所以对于 cpu 密集的场景需要考虑技术的选型。

参考

  • 并发模型与事件循环 - JavaScript | MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop
  • 事件循环:微任务和宏任务: https://zh.javascript.info/event-loop
  • 关于JavaScript单线程的一些事: https://github.com/JChehe/blog/blob/master/posts/%E5%85%B3%E4%BA%8EJavaScript%E5%8D%95%E7%BA%BF%E7%A8%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BA%8B.md
  • 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理: https://segmentfault.com/a/1190000012925872
  • 这一次,彻底弄懂 JavaScript 执行机制 - 掘金: https://juejin.cn/post/6844903512845860872
  • JavaScript main thread. Dissected. : https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23
  • JavaScript Event Loop And Call Stack Explained: https://felixgerschau.com/javascript-event-loop-call-stack/
  • Javascript Engine, Call Stack, Callback Queue, Web API and Event Loop: https://simonzhlx.github.io/js-engine/
  • 微任务、宏任务与Event-Loop - 掘金: https://juejin.cn/post/6844903657264136200
  • 简述js中的同步阻塞和异步非阻塞 - 掘金: https://juejin.cn/post/6844903850483138568

相关参考

水泵盘根型号(水泵基础知识(四))

...型泵性能参数、水力模型。适用于供暖及空调系统冷热水循环、高层建筑给水、消防管路增压、远距离输水、工业生产工艺循环增压、园林喷灌、介质为清水或物理、化学性质类似于清水的其它介质。BTLR为单级单吸立式热水离...

热水离心泵的型号大全(冷却循环泵型号及介绍)

一、ISG低温冷却循环泵产品介绍ISG低温冷却循环泵系在ISG型基础上并参照IS型离心泵之性能参数,根据管道式离心泵的独特结构组合设计制造。该产品采用国内水泵专家提供的最先进水力模型,高效节能,性能可靠,深受国内外...

热水离心泵的型号大全(冷却循环泵型号及介绍)

一、ISG低温冷却循环泵产品介绍ISG低温冷却循环泵系在ISG型基础上并参照IS型离心泵之性能参数,根据管道式离心泵的独特结构组合设计制造。该产品采用国内水泵专家提供的最先进水力模型,高效节能,性能可靠,深受国内外...

循环水泵使用说明(循环水泵启停有何规定?)

循环水泵是火力发电厂的主要设备之一,循环水泵的启停操作在发电厂是时常出现的,启停是否正常影响着机组的安全运行。根据平时对用能单位的调查得出发电厂对循环水泵的正常启停都有着比较严格的规定。1.把循环水泵的...

循环水泵的作用(循环水泵的作用)

循环泵广泛用于需要提供循环冷却介质的各种场合,例如带有循环泵的典型涡轮机,用于汽车的循环水泵,大型空调装置等。循环泵有大,小和微型三种体型分类,您可以根据场合选择合适的尺寸。潜水循环泵,用于输送原水,...

水泵型号参数表示什么意思(循环水泵选型手册)

循环水泵可以使水在系统内不断地循环,降低阻力损失,和建筑物的高度没有直接联系。循环泵广泛适用于化工、电站、发电厂、轻纺等领域,有些人会出现循环水泵选型难的问题。循环水泵选型难不要怕,我们有十年资深工程...

循环泵三个档怎么用图解(循环水泵启停有何规定?)

循环水泵是火力发电厂的主要设备之一,循环水泵的启停操作在发电厂是时常出现的,启停是否正常影响着机组的安全运行。根据平时对用能单位的调查得出发电厂对循环水泵的正常启停都有着比较严格的规定。1.把循环水泵的...

暖通工程循环泵(什么是循环水泵?怎么选择?)

  今天,上海沈泉就来为大家简单的讲解下什么是循环水泵及循环水泵怎么选型的知识内容,大家请跟着小编一起来看看下面的内容吧。  什么是循环水泵?  循环水泵:主要用于化工装置中输送、反应、吸收、分析、酸...

水泵正常运行时关闭出口阀门(循环水泵操作规程)

循环水泵的操作规程是什么?通过阅读本篇文章,我们将简要介绍循环水泵操作规程。本规程规定了循环水泵的操作步骤及注意事项。启动前准备工作1、检查电路系统,电源电压是否正常。2、检查泵机油盒内机油是否在机油看...

水泵扬程流量对照表(循环水泵流量和扬程之间的关系)

循环水泵流量和扬程是水泵重要的型号性能参数,假如不清楚水泵流量和扬程之间的关系,在为用户选泵时就不能很好的选出泵型。循环水泵流量扬程性能曲线图循环水泵流量和扬程的关系循环水泵流量循环水泵流量是指在单位...