「Learning」浏览器多进程与JS运行机制

主要内容:

  1. 区分进程和线程
  2. 浏览器是多进程的(哪些进程,优势是什么,渲染进程的常驻线程,内核通信过程)
  3. 梳理浏览器内核中线程之间的关系
  4. JS运行机制
  5. 宏任务与微任务

学习内容:《 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理》By 撒网要见鱼

Learning Card

1. 区分进程和线程

进程 线程
有系统分配的内存(独立资源)
进程/线程之间相互独立
进程
  • cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
  • 进程之间相互独立
  • 一个进程由一个或多个线程组成
线程
  • cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位)
  • 多个线程在进程中协作完成任务
  • 同一个进程下的各个线程之间共享程序的内存空间

我的理解:

其实可以用一个比较常用的软件来打比方。我们把CPU想象成我们自己,把程序都想象成EXCEL表格。一个进程就是一个excel文件,每个excel文件之间是相互独立的。打开一个excel文件,里面会有sheet,这个sheet就是线程。一个excel文件里可能有1个sheet,也可能有多个sheet,这就是单线程和多线程

我们可以同时打开很多个excel文件,但是一次只能处理一个,这就是并发。也许有一天,人工智能发展到一定的阶段,可以虚拟出一个“我”,这时,好几个我可以同时处理不同的Excel文件,这就是并行


2. 浏览器是多进程的

  • 浏览器是多进程的
  • 浏览器有一个主进程
  • 每个tab页有一个独立的进程(浏览器的优化机制可能会合并进程,这一条并不绝对)
浏览器包含哪些进程
  1. Browser主进程(只有一个)
  2. 第三方插件进程(仅当使用时创建)
  3. GPU进程(最多一个,用于3D绘制)
  4. 浏览器渲染进程(默认每个tab一个)
浏览器多进程的优势
  • 避免单个页面或插件crash影响整个浏览器
  • 充分利用多核优势(避免资源浪费)
  • 方便使用沙盒模型隔离插件等进程
浏览器内核(渲染进程)的常驻线程
  1. GUI渲染线程(渲染界面,重绘和回流)
  2. JS引擎线程(解析JS代码)
  3. 事件触发线程(控制事件循环,如AJAX等)
  4. 定时器触发线程(setTimeOut, setInterval,用来计时并触发定时)
  5. 异步HTTP请求线程(XMLHttpRequest连接)

注意点:

  1. GUI渲染线程与JS引擎线程是互斥的。JS引擎执行时GUI会被挂起,GUI更新会保存在一个队列中等JS引擎空闲时立即被执行;同样,JS执行时间过长会导致页面渲染不连贯
  2. 事件触发线程归属于浏览器而不是JS引擎

我的理解:

  1. GUI渲染线程负责解析HTML,CSS,形成界面
  2. JS引擎线程负责解析JS代码,如果是可以直接处理的代码,JS引擎会直接解析,如JS代码创建一个新的元素,添加在原有的div里
  3. JS引擎线程遇到无法立即处理的代码(异步操作,如setTimeOut, AJAX等),则放入事件处理线程,事件处理线程并不实际处理它们,只在事件符合触发条件时,将事件放入待处理队列,JS引擎空闲时会处理待处理队列
  4. 可以理解为JS引擎线程用于处理事件,事件触发线程用于安排事件如何排队
  5. 定时器触发用于计时和触发定时,等事件符合触发的时间时,将事件加入到待处理队列中,它同样是用来规定如何排队的,只是排队方式和事件处理线程不太一样(这也可以解释为什么setTimeOut并不是规定几秒之后执行,而是规定几秒之后加入到待处理队列中,如此时JS引擎线程并不空闲,那么时间并不会被马上处理,因此事件被处理的时间可能是长于我们预设的时间的)
  6. 异步http请求线程也是一样,它在XMLHttpRequest连接之后,检测到状态改变,将回调函数放入到事件队列中
  7. 可以看到事件处理线程、定时触发器线程、异步http请求线程本质上都是安排对应事件在符合触发条件之后,将反馈的,即需要处理的事件放入到待处理事件队列中,由JS引擎来执行

或许这样说更容易理解:

我们把这些线程理解成做菜的厨子。

  • GUI渲染线程就是负责摆盘的,JS引擎线程是负责做菜的主厨,他们互相不能干预对方
  • JS引擎线程遇到可以直接做的原料就自己动手把菜给炒了
  • 有些原料不能马上做,可能需要等原料温度降到0度才可以动手,于是事件处理线程就等原料温度降到对应温度再交给主厨——JS引擎线程
  • 有些原料必须腌制10分钟之后才能做,于是就该定时触发器线程负责,等到10分钟后把原料交给JS引擎线程
  • 还有些原料必须打电话联系供应商送来才能做,于是就先给他们打电话确认可以送货(XMLHttpRequest),等到货送到了(状态改变),再烧高汤并把高汤交给主厨来做,这个对应的高汤就是异步http请求的回调函数

浏览器的进程与线程

Browser进程和浏览器内核(Renderer进程)的通信过程

浏览器进程与渲染进程的通信

3. 梳理浏览器内核中线程之间的关系

GUI渲染线程与JS引擎线程互斥
  • JS可以操作DOM,如果边操作DOM边渲染会出错
JS阻塞页面加载
  • JS操作时,GUI渲染线程被挂起
  • JS操作时间过长,看起来就像GUI渲染卡住了
WebWorker
  • 创建一个Worker()对象来运行命名的JS文件,即申请开一个子线程
  • workers在另一个全局上下文中,不同于当前的window
  • JS还是单线程,但Worker线程里面不会影响JS引擎主线程
WebWorker与SharedWorkder
  • WebWorker是tab页即render进程下的线程
  • SharedWorker是浏览器的一个进程

我的理解:

  • JS是单线程
  • JS引擎和GUI渲染一次只能执行一个,另一个被挂起
  • 这样会导致如果JS引擎执行时间太长,GUI看起来就卡住不动了
  • 为了解决这个问题,提出了WebWorker这个解决方案
  • WebWorker是一个子线程,专门用来计算运行JS文件

4. JS运行机制

  • 同步任务都在主线程上执行
  • 主线程运行时产生执行栈
  • 事件触发线程管理着一个任务队列,异步任务有了运行结果,就在任务队列尾部放置事件
  • 栈中任务执行完毕,回去读取事件队列中的事件
setInterval的问题
  • 累计效应:执行时间比预期小,可能导致连续运行没有间隔

5. macrotask和microtask

宏任务
  • 每次执行栈执行的代码就是一个宏任务

  • 完成后渲染,再执行下一个

微任务
  • 宏任务执行结束后立即执行的任务

  • 在渲染之前执行

类型
宏任务 setTimeOut, setInterval
微任务 Promise, process.nextTick

宏任务、微任务.png