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引擎,用来控制事件循环

定时触发器线程

  • setTimeoutsetInterval所在的线程
  • 定时任务并不是由 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 是浏览器所有页面共享的(由单独的进程管理,与渲染进程一个级别)

参考资料