浏览器进程与线程

一、前言

前端工程师在面试过程中,面试官让你讲一讲浏览器进程和线程;

前端开发会涉及异步Promise、延迟setTimeout的使用,同时还会有关于异步面试题的考察;

同时也为了代码的正确书写与代码优化,很有必要深入了解浏览器进程与线程相关问题。

二、什么是进程与线程

进程是系统中正在运行的一个程序,程序一旦运行就是进程,它是资源分配的最小单位。

浏览器中每个tab相当于独立的一个进程,每个进程都拥有独立的地址空间,一个进程无法访问另一个进程的变量和数据结构。

一个进程可以拥有多个线程,每个线程使用其所属进程的栈空间。

线程是进程的一个实体,是进程的一条执行路径,它是CPU调度的最小单位。

浏览器中每个tab相当于独立的一个进程,每个进程又包含有多个线程。

上述是《计算组成原理》中对进程和线程给出的解释。

为了方便大家理解可以简单理解为:进程=火车,线程=车厢

  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难进行共享(一辆火车上的乘客很难换到另外一辆火车)浏览器也一样,每打开一个tab就对应一个进程,进程间数据不共享。
  • 同一进程下不同线程间数据很易共享(1车厢可以走到3车厢)浏览器同样有js线程和html线程,共享一些数据才能正常的渲染界面。

三、Chromium多进程

笔者通过运行中的Chrome浏览器来帮助读者理解多进程模型,主要操作步骤如下:

1、打开Chrome浏览器输入www.baidu.com

2、linux系统命令行输入 ps -ef | grep chrome,得到关于Chrome浏览器的进程如下:

浏览器主要有如下进程:

1、浏览器进程(Browser进程)

上图中pid=28458对应的进程即为Browser进程,与其他进程的区别是它显示在第一个并且没有参数,其他进程尾部附带--type=xxx。

它是浏览器的主进程,负责协调、主控其他进程,其作用包含:

  • 负责各个页面的管理,创建和销毁其他进程;
  • 负责浏览器界面显示,与用户交互,如前进,后退等;
  • 网络资源的管理、下载等;

2、GPU进程

上图中pid=28813对应的进程为GPU进程,后缀附带参数--type=gpu-process。

 用于3D绘制,有且只有一个,可以禁用。

3、浏览器渲染进程(renderer进程)

上图中pid=28694对应的进程为renderer进程,后缀附带参数--type=renderer,浏览器渲染进程(render进程),即通常说的浏览器内核,主要作用是:页面渲染、脚本执行、事件处理等。
每一个标签页的打开都会创建一个Render进程,并且互不影响。默认的话一个标签页对应一个Render进程,但是,有时候浏览器会将多个进程合并,如打开了多个空白标签页。
除此之外还有辅助进程(utility)、插件进程(Plugin)等。

四、Chromium多线程

下面以linux平台的Chromium浏览器为例,介绍Browser进程和Renderer进程中包含线程的情况。

1、首先来看Browser进程,该进程包含了非常多的线程,多达25个。

其中SPID=28458为主线程,SPID=28508为IO线程。中间有很多线程用来处理视频、存储、网络、文件、音频、浏览历史等。

2、浏览器渲染进程中的线程

下面是Renderer进程中的线程信息,其中SPID=28694为主线程,SPID=28707为IO线程,

浏览器渲染进程(render进程,也成为浏览器内核),是我们前端开发人员最需要关注的。它主要的作用:页面渲染、脚本执行、事件处理等。它包含一下5种线程:

1).GUI渲染线程(有且只有一个)

  • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等
  • 当界面需要重绘(repain)时或者回流(reflow),该线程就会执行
  • 注意:GUI线程和JS引擎线程互斥!当JS引擎执行时,GUI线程就会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时,立即被执行。

为什么JS引擎执行的时候GUI线程要被挂起?

因为JS是可以操作DOM的,而如果在修改这些元素的同时渲染界面,即当这两个线程不是互斥的时候,那么GUI渲染线程前后获得的元素数据就可能不一致。所以JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎线程空闲时立即被执行

2).JS引擎线程(有且只有一个)

  • JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行Js代码;
  • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序;
  • 注意:GUI线程和JS引擎线程互斥!所以当JS加载时间过长时,会造成页面渲染不连贯,导致阻塞页面加载。这就是为什么建议将<script>标签写在<body>的最末端。

3).事件触发线程

  • 事件触发线程归属于浏览器,而不是属于JS引擎,JS引擎处理的事务过多,需要浏览器另开线程来进行协助
  • 事件触发线程是采用事件驱动机制,来响应用户操作的,事件触发线程通过维护事件循环和事件队列等的方式,来响应和处理事件
  • 当处理一些不能立即执行的函数或者其他的代码时,会将对应的任务在其可以触发的时机,添加到事件队列的末端
  • 事件循环机制会在JS引擎线程空闲时,循环访问事件队列的头部,如果有函数,则会将该函数推到执行栈中并立即执行。

4).定时器触发线程(多个)

  • setIntervalsetTimeout所在线程;
  • 浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确性;
  • 因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;

5).异步http请求线程(多个)

  • XMLHttpRequest连接后通过浏览器新开一个线程请求;
  • 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行;

3、对于GPU等进程来说,结构要简单很多,基本就一个主线程和IO线程,请读者自行查阅。

五、浏览器多进程与多线程UML图

以上讲述了浏览器多进程模型并列举了浏览器进程(Browser)和渲染进程(Renderer)的多线程以及各线程的作用,下面以图示来说明进程与线程间的关系:

注:本图只是关于浏览器多进程的简图,只为方便读者更好理解进程与线程间的关系

六、js为什么是单线程

JavaScript的主要用途是用户交互和操作DOM。线程之间资源共享,相互影响,如果JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时这两个节点会有很大冲突,为了避免这个冲突,所以决定了它只能是单线程,否则会带来很复杂的同步问题。
 

 

posted on 2021-05-16 18:17  程序员阿田  阅读(434)  评论(0编辑  收藏  举报

导航