Skip to content

渲染进程中的线程 ​

写在面前 ​

上一章我们介绍了 浏览器架构 。介绍了现代浏览器的架构为多进程架构,它由浏览器主进程、GPU 进程、网络进程、音频进程、存储进程、多个渲染进程和多个插件进程组成(89.0.4389.90 版本 Chrome)。我们也说了线程的崩坏会导致页面(渲染进程)卡死,但不会影响其他页面。

那么渲染进程有什么组成?这个很重要,因为它是页面渲染、事件循环的基础。

一句话解释 ​

渲染进程就是我们常说的浏览器内核,负责页面渲染,脚本执行,事件处理等,每个 tab 页就是一个渲染进程

渲染进程中包括 GUI 渲染线程、 JS 引擎线程、事件触发线程、网络异步线程、定时器线程

渲染进程内部包含主线程、工作线程、合成线程和光栅线程

由什么组成 ​

GUI 渲染线程 ​

  • 负责渲染浏览器界面,解析 HTML、CSS,构建 DOM 树和 RenderObject 树,布局和绘制等
  • 当界面需要重绘或由于某种操作引发回流时,该线程就会执行
  • 注意,GUI 渲染线程与 JS 引擎线程时互斥,当 JS 引擎执行时 GUI 线程会被挂在,GUI 更新会被保存在一个队列中等待 JS 引擎空闲时立即被执行

JS 引擎线程 ​

  • 也称为 JS 内核,负责处理 JavaScript 脚本程序(例如 V8 引擎)
  • 负责处理解析和执行 JavaScript 脚本程序
  • 只有一个 JS 引擎线程(单线程)
  • 与 GUI 渲染线程互斥,防止渲染结果不可预期

事件触发线程 ​

  • 用来控制事件循环(鼠标点击、setTimeout、Ajax 等)
  • 当事件满足触发条件时,将事件放入到 JS 引擎所在的执行队列中
  • 归属于浏览器而不是JS引擎,用来控制事件循环

定时触发器线程 ​

  • setTimeout和setInterval所在的线程
  • 定时任务并不是由 JS 引擎计时的,是由定时触发线程来计时
  • 计时完毕后,通知事件触发线程

异步 HTTP 请求线程 ​

  • 浏览器有一个单独的线程用于处理 AJAX 请求
  • 当请求完成时,若有回调函数,通知事件触发线程

为什么 GUI 渲染线程与 JS 引擎线程互斥 ​

这是由于 JS 是可以操作 DOM 的,如果同时修改元素属性并同时渲染界面(即 JS 线程 和 UI 线程同时运行),那么渲染线程前后获得的元素就可能不一致了

JS阻塞页面加载以及 React Fiber 架构的出现 ​

因为 GUI 渲染线程和 JS 引擎线程是互斥的,所以如果 JS 执行时间过长就会出现页面卡顿现象

比如 React15 采用的递归更新,当组件过大时,意味着JS引擎线程需要花很多时间去执行,这样导致 GUI 不能绘制页面,不能绘制页面就呈现卡顿现象

所以才有了后面的 React Fiber 架构,采用时间分片才解决大组件更新问题

WebWorker ​

我们讲到 JS 引擎是单线程,当 JS 执行时间很长时,页面无法得到响应体检变差,那么解决方案是什么?

HTML5 提出了 Web Worker,浏览器提供给了一个子线程,让其可以与 JS 引擎相互通信

所以,当有比较耗时的工作,就可以单独开一个 Worker 线程,待计算出结果后,将结果通信给 JS 引擎线程即可(通过 postMessage API)

除了 WebWorker 外,还有 SharedWorker,两者的区别在于:

  • WebWorker 只负责某个页面,它相当于是某个渲染进程(一个页面就是一个渲染进程)下的一个线程
  • SharedWorker 是浏览器所有页面共享的(由单独的进程管理,与渲染进程一个级别)

参考资料 ​