阅读 95

浅谈浏览器进程、线程

1、并发的概念

并发简而言之:多个任务同时执行

大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行

任务执行的一小段时间叫时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态,等待下一个属于它的时间片的到来

这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发

2、进程、线程

2.1 进程的概念

一个进程就是一个程序的运行实例。具体来说就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程

进程中的任意一线程执行出错,都会导致整个进程的崩溃

进程之间的内容相互隔离。但是进程间可以通过进程间通信(IPC)进行通信

2.2 线程的概念

线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元

线程是进程内的一个独立执行单元,在不同的线程之间是可以共享进程资源的

线程之间可以对进程的公共数据进行读写操作

所以在多线程的情况下,需要特别注意对临界资源的访问控制

主线程是由系统进程所创建的,同时用户也可以自主创建其它线程,这一系列的线程都会并发地运行于同一个进程中

在系统创建进程之后就开始启动执行进程的主线程,而进程的生命周期和这个主线程的生命周期一致,主线程的退出也就意味着进程的终止和销毁

3、单进程浏览器

07年前,市面上基本都是单进程浏览器。单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript运行环境、渲染引擎和页面等

这样的浏览器存在的问题有:不稳定、不流畅、不安全

  • 不稳定:因为进程内一个线程奔溃,会导致整个进程奔溃。
  • 不流畅:浏览器内所有模块都运行在一个线程中,也就意味着同一时刻只能有一个模块可以执行
  • 不安全:因为只有一个进程,所以进程内的数据都可以被共享,而且插件可以操作一些底层的东西

4、新生代浏览器

4.1 概述

新生代浏览器,是多进程、多线程模式的

一般一个tab页为一个进程,当然所有空白页会被合并成一个进程

谷歌浏览器中,有Browser进程、GPU进程、插件进程、渲染进程(Render进程)

  • Browser进程:浏览器的主进程(负责协调、主控),只有一个。作用有:

    • 负责浏览器界面显示,与用户交互。如前进,后退等
    • 负责各个页面的管理,创建和销毁其他进程
    • 网络资源的管理,下载等
  • 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建

  • GPU进程:最多一个,用于3D绘制等

  • 浏览器渲染进程(浏览器内核):Renderer进程,内部是多线程的,也就是我们每个标签页所拥有的进程,互不影响,负责页面渲染,脚本执行,事件处理等

4.2 渲染进程(浏览器内核)

又名Render进程,主要用来处理页面信息的计算和展示,因此通常也被称为渲染引擎

因为在Chrome浏览器中,每一个tab页为一个单独的进程,因此每个tab页都有其独立的渲染引擎实例

该进程下拥有多个线程:js引擎线程GUI渲染线程事件触发线程http请求线程定时触发器线程

键盘、鼠标I/O输入输出事件、窗口大小的resize事件、定时器(setTimeout、setInterval)事件、Ajax请求网络I/O回调等。当这些异步任务发生的时候,它们将会被放入浏览器的事件任务队列中去,所以异步是浏览器的两个或者两个以上线程共同完成的。比如ajax异步请求和setTimeout

  • onclick 由浏览器内核的 DOM Binding 模块来处理,当事件触发的时候,回调函数会立即添加到任务队列中
  • setTimeout 会由浏览器内核的 timer 模块来进行延时处理,当时间到达的时候,才会将回调函数添加到任务队列中
  • ajax 则会由浏览器内核的 network 模块来处理,在网络请求完成返回之后,才将回调添加到任务队列中`
4.2.1 JS引擎线程

js引擎有多个线程,一个主线程,其他的配合主线程

  • 主要负责处理Javascript脚本程序,例如V8引擎。Javascript引擎线程理所当然是负责解析Javascript脚本,运行代码
  • 等待任务队列的任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS引擎在运行JS程序

js引擎的单线程:

js代码始终在一个线程上执行,此线程被称为js引擎线程

因为js处理了页面中用户的交互,以及操作DOM树、CSS样式树等。如果JS是多线程的,那么多线程同步处理的时候,就可能存在同时操作一个DOM的临界情况。所以为了避免带来设计上更大的复杂性,所以JS在最初设计的时候选择了单线程执行

4.2.2 GUI渲染线程
  • 负责渲染浏览器界面,包括解析HTML、CSS、构建DOM树、Render树、布局与绘制等
  • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

在js引擎运行脚本期间,GUI渲染线程会被挂起,GUI更新会被保存在一个队列中等待引擎线程空闲时立即被执行

如果JS引擎一直处于计算状态,这个就会导致渲染进程一直不能执行渲染,页面看起来就会卡顿,渲染不连贯,因此要避免JS执行时间过长

如果需要进行一些高耗时的计算时,可以单独开启一个WebWorker线程

4.2.3 事件触发线程
  • 归属于浏览器而不是JS引擎,用来控制交互、响应用户、控制事件循环
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
  • 这些事件可以是定时任务(setTimeOut),也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等
  • 由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
4.2.4 异步HTTP请求线程
  • 在XMLHttpRequest连接后是通过浏览器新开一个线程请求
  • 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,等待JavaScript引擎执行
4.2.5 定时触发器线程
  • setIntervalsetTimeout所在线程
  • 浏览器定时计数器并不是由JavaScript引擎计数的(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
  • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
  • W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms (4ms是为了实现tradeoff(平衡)而设定的)

5、WebWorker

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢

当我们创建一个新的worker时,改代码会运行在一个全新的javascript的环境中(WorkerGlobalScope)运行,是完全和创建worker的脚本隔离,这时我们可以吧创建新worker的脚本叫做主线程,而被创建的新的worker叫做子线程

WorkerGlobalScope是worker的全局对象,所以它包含所有核心javascript全局对象拥有的属性如JSON等,window的一些属性,也拥有类似于XMLHttpRequest()等

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭

5.1 创建线程

在创建线程的时候需要给实例化的Worker传入唯一一个参数,指向一个javascript文件资源的url或者Blob对象(Blob对象就是一个包含有只读原始数据类文件对象),调用这个构造函数之后,一个线程就被创建了,如下:

var worker = new Worker("worker.js");
var worker = new Worker(blob);
复制代码

5.2 线程通信

Web Worker的基本原理就是在当前的主线程中加载一个只读文件来创建一个新的线程,两个线程同时存在,且互不阻塞,并且在子线程与主线程之间提供了数据交换的接口postMessageonmessage。来进行发送数据和接收数据。其数据格式可以为结构化数据(JSON等)

5.3 WebWork使用限制

  • 同源限制: 分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源

  • DOM 限制: Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用documentwindowparent这些对象。但是,Worker 线程可以navigator对象和location对象

  • 通信联系: Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

  • 脚本限制: Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求

  • 文件限制: Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络

5.4 共享线程 SharedWorker

共享线程是为了避免线程的重复创建和销毁过程,降低了系统性能的消耗,共享线程SharedWorker可以同时有多个页面的线程链接

使用SharedWorker创建共享线程,也需要提供一个javascript脚本文件的URL地址或Blob

6、4ms限制

简而言之就是为了实现tradeoff(平衡)而设定的

其实严格上,不应该说4ms限制,因为最低延迟的限制,会因不同浏览器厂商的实现而不同。

如果将最低延迟设置为0ms的话,会导致js引擎过渡循环,进而导致cpu-spinning(过载),因为然后你设置了n个超久的定时器,在这个时间段内,js引擎将不断地去轮询定时是否终止,而在轮询过程中,理应有个节流过程,我们称之为计时器节流,这个也就是所谓的最低延迟4ms,其实4ms延迟就是计时器轮询的节流方案


参考文献

阮一峰 Web Worker 使用教程

Web Worker 详细介绍

为什么 setTimeout 有最小时延 4ms ?

多进程浏览器、多线程页面渲染与js的单线程

文章分类
前端
文章标签