前言
在文章开始之前,我们有必要简单了解下线程与进程的概念以及计算机的并行处理。
有必要的话也可以了解一下浏览器的组成部分有什么? 主流浏览器的内核与JS引擎的种类(浏览器的组成部分有什么? 主流浏览器的内核与JS引擎的种类)
用较为官方的术语描述一遍:
进程是cpu资源分配的最小单位,也是独立运行的最小单位(一个进程就是一个程序的运行实例)
线程是cpu调度的最小单位(线程是不能单独存在的,它是由进程来启动和管理的,一个进程中可以有多个线程,进程内的线程共享进程全部资源)
打个比喻:
1. 进程是一个工厂,工厂有它的独立资源
2. 工厂之间相互独立
3. 线程是工厂中的工人,多个工人协作完成任务
4. 工厂内有一个或多个工人
5. 工人之间共享空间
再完善完善概念:
- 工厂的资源 -> 系统分配的内存(独立的一块内存)
- 工厂之间的相互独立 -> 进程之间相互独立
- 多个工人协作完成任务 -> 多个线程在进程中协作完成任务
- 工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成
- 工人之间共享空间 -> 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)
计算机的并行处理
计算机中的并行处理就是同一时刻处理多个任务,比如我们要计算下面三个表达式的值并显示出结果。
A = 1+2
B = 20/5
C = 7*8
在运行代码的时候,我们可以把这个过程拆分成四个任务:
任务一:计算A = 1+2
任务二:计算B = 20/5
任务三:计算C = 7*8
任务四:显示最后计算的结果
正常情况下程序可以使用单线程来处理,也就是分四步按照顺序分别执行这四个任务。
如果采用多线程,会怎么样呢?我们只需分”两步走“:第一步,使用三个线程同时执行前三个任务;第二步,再执行第四个显示任务。
通过对比分析,你会发现用单线程执行需要四步,而使用多线程只需要两步,因此,使用并行处理能大大提升性能。
线程VS进程
多线程可以并行处理任务,但是线程是不能单独存在的,需要进程来启动和管理,那么什么是进程呢?实际上一个进程就是一个程序的运行实例,具体就是启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的运行环境叫进程。
单线程VS多线程
单线程:事情只能做完一件再做下一件事(要是发起请求资源,很久才有回应,也只能这么耗着)
多线程:只作单处理器情况下的说明(虽有多核浏览器,但实时处理着线程的只有单个核),A线程进行耗时请求,那我不等你返回,切到B线程,先执行B了,这样就提高了整体运行效率。(实际没有这么简单,要进行时间分片,状态监控等等)
我们将前文并行处理中提到的计算过程用下图来对比:
单进程浏览器
单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。早在 2007 年之前,市面上浏览器都是单进程的,现在市面上浏览器基本都是多进程的了。
所有功能模块放在一个进程里面,是导致单进程浏览器不稳定、不流畅和不安全的主要原因。
不稳定
举个例子:进程里面的某关键功能崩了,那就不是这个网页打不开的问题了,而是整个浏览器都炸了。
类似于js中一个函数的执行,某个地方一出错,整个函数就执行不下去了
不流畅
举个例子:所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的。假如JS正在处理一个耗时计算,当其执行时,它会独占整个线程,这样导致其他运行在该线程中的模块就没有机会被执行,这样就会导致整个浏览器失去响应,变卡顿。
不安全
举个例子:插件可以使用 C/C++ 等代码编写,插件也是浏览器功能模块,和其他模块共享进程资源,通过插件可以获取到操作系统的任意资源。
多进程浏览器(以谷歌浏览器为例)
每打开一个新页面就会创建一个进程,当然也会有些优化策略,如果同时打开多个空白页,会将多个空白页合并为一个进程。
查看浏览器进程
步骤:谷歌浏览器->更多工具->任务管理器。
下图你可以确切的看到浏览器当前运行的各个进程
默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。但是我们也能看到有几个标签页共用一个进程,这其实是浏览器的优化机制,将某些同域名下的进程合并为一了。 详情可看这篇文章(谁说一个Tab下就只有一个渲染进程的?渲染进程个数究竟与什么有关系呢)
浏览器中主要的进程
从图中可以看出,Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、1个音频(Audio)进程、多个渲染进程和多个插件进程。
浏览器进程:浏览器的主进程(负责协调、主控)
1. 负责浏览器界面显示,与用户交互。网址栏输入、前进、后退等
2. 管理各个页面,创建和销毁进程
3. 将页面内容(位图)写入到浏览器内存中,最后再将图像显示在屏幕上
4. 文件储存等功能
渲染进程:默认一个Tab页面一个渲染进程(特殊情况请看:谁说一个Tab下就只有一个渲染进程的?渲染进程个数究竟与什么有关系呢?)
1.页面渲染
2. 脚本执行
+3.事件处理等
GPU进程:
用于3d绘制等,将开启了3d绘制的元素的渲染由CPU转向GPU,也就是开启GPU加速,最多一个
网络进程:
主要负责页面的网络资源加载,之前是作为一个模块(NetWork Thread)运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
插件进程:
每种类型的插件对应一个进程,仅当使用该插件时才创建
音频进程:
浏览器音频管理
浏览器内核 指的是渲染引擎,以前可以分成两部分:渲染引擎和JS引擎,后来JS引擎独立出来了。
服务类进程是常驻的 对某服务进程点击了结束进程,它也会立即重启或在你刷新页面时重启。
举个例子:
以Audio Service为例,你打开某页面在播放音频或视频的时候,直接结束了Audio Service进程,你会发现Audio Service瞬间消失又启动了,与此同此,视频暂停了,点击继续播放也没用,需要刷新页面后才可以正常播放。
怎么证明谷歌浏览器页面是依赖于GPU绘制的呢(硬件加速模式开启的情况下)?
结束GPU进程瞬间你可以看到整个浏览器全黑屏,瞬间进程自动重启,浏览器恢复正常。
多进程浏览器的优势
与单进程浏览器相比:单进程浏览器的不足多进程浏览器都处理好了
1.避免单个页面或者插件崩溃影响整个浏览器
2.充分利用多核的优势
3.使用沙盒模型隔离插件等进程,提高浏览器稳定性
不足之处就是占用的内存多了(空间换时间)
重点——浏览器内核(渲染进程)
再推荐这篇文章(谁说一个Tab下就只有一个渲染进程的?渲染进程个数究竟与什么有关系呢)
页面的渲染,JS的执行,事件的循环,都在这个进程内进行。
浏览器的渲染进程是多线程的,并非所有浏览器中渲染进程的线程划分都是一样的,谷歌浏览器总是走在浏览器发展的前沿,经常会更快迭代引领潮流。
1.非谷歌浏览器
GUI渲染线程与JS引擎线程是互斥的,不能一并执行。这是避免页面渲染过程中,JS修改了DOM元素,使得页面呈现效果与预想不符。还有就是,假如JS在执行大规模的预算,那岂不是会将页面渲染卡住了吗?为此H5引进了Web Workers,web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。
JS为什么是单线程?
如果JS是多线程,假如有thread1和thread2两个线程同时操作同一个Dom元素,thread1删除该Dom元素,thread2修改该Dom元素,同时下达两个矛盾任务让浏览器怎么执行呢,这就是为什么JS被设计成单线程。
2.谷歌浏览器
我们再看下谷歌浏览器官网提供的渲染进程里的线程图:
信息来源: https://developers.google.com/web/updates/2018/09/inside-browser-part3
主线程 Main thread
主线程中即可以执行 JS ,也可以执行渲染工作,还可以执行其他工作,但是由于是主线程是只是一个线程,所以各个工作不能同时执行,只能按照一定的先后次序执行。 MDN上有一个简单介绍:
工作线程 Worker thread
由 Worker 或 Service Worker 注册的 javascript 代码会有单独的 Worker 线程处理,独立于主线程。
排版线程 Compositor thread
页面平滑层次展示
光栅线程 Raster thread
页面快速呈现
基本而言,谷歌浏览器的主线程,操办了非谷歌浏览器中那五个线程的任务,且额外还新增了几个线程。
拓展链接:页面的渲染流程(谷歌前沿版
总结一下:
1. 一个程序至少有一个进程,一个进程至少有一个线程
2. 线程的划分尺度小于进程,使得多线程程序的并发性高
3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别