浏览器的进程与线程

该篇文章都是一些定义和文案解释,比较枯燥,但只要多读几遍,相信一定能给大家带来意想不到到的收获。

一、进程和线程的概念

首先我们要知道线程和进程的概念:
进程:CPU 进行资源分配的基本单位
线程:CPU 调度的最小单位

这是进程和线程最官方也是最常见的两个定义,但是这两个概念太抽象了,很难以理解。
通俗一点讲:进程可以描述为一个应用程序的执行程序,线程则是进程内部用来执行某个部分的程序。
下面再引用一段知乎的高赞回答,我感觉非常有意思:
做个简单的比喻:进程=火车,线程=车厢

1、线程在进程下行进(单纯的车厢无法运行)
2、一个进程可以包含多个线程(一辆火车可以有多个车厢)
3、不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
4、同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
5、进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
6、进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,
但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
7、进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
8、进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-“互斥锁”
9、进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

二、应用程序如何调度进程和线程

当一个应用程序启动时,一个进程就被创建了。应用程序可能会创建一些线程帮助它完成某些工作,
但这不是必须的。操作系统会划分出一部分内存给这个进程,当前应用程序的所有状态都将保存在这个私有的内存空间中。
当你关闭应用时,进程也就自动蒸发掉了,操作系统会将先前被占用的内存空间释放掉。
一个程序并不一定只有一个进程,进程可以让操作系统再另起一个进程去处理不同的任务。当这种情况发生时,
新的进程又将占据一块内存空间。当两个进程需要通信时,它们进行进程间通讯。
许多应用程序都被设计成以这种方式进行工作,所以当其中一个进程挂掉时,它可以在其他进程仍然运行的时候直接重启。

三、进程和多线程

理解了上面的内容,我们再来重新梳理多进程和多线程的概念:

多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。
多进程带来的好处是明显的,比如你可以听歌的同时,打开编辑器敲代码,编辑器和听歌软件的进程之间丝毫不会相互干扰。

多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,
也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

四、Chrome 的多进程架构

由于浏览器本身没有统一的规范,不同的浏览器之间的架构可能完全不同,在浏览器刚被设计出来的时候,
那时的网页非常的简单,每个网页的资源占有率是非常低的,因此一个进程处理多个网页时可行的。
然后在今天,大量网页变得日益复杂。把所有网页都放进一个进程的浏览器面临在健壮性,响应速度,
安全性方面的挑战,所以大部分现代浏览器都是多进程的。
从上面的图我们可以很明显的看出 Chrome 是一个多进程的架构,
我们打开一个浏览器时会启动多个不同的进程协助浏览器将页面为我们呈现出来:

浏览器进程
插件进程
GPU进程
渲染进程

1.浏览器进程
浏览器最核心的进程,负责管理各个标签页的创建和销毁、页面显示和功能(前进,后退,收藏等)、网络资源的管理,下载等。

2.插件进程
负责每个第三方插件的使用,每个第三方插件使用时候都会创建一个对应的进程、
这可以避免第三方插件crash影响整个浏览器、也方便使用沙盒模型隔离插件进程,提高浏览器稳定性。

3.GPU进程
负责3D绘制和硬件加速

4.渲染进程
浏览器会为每个窗口分配一个渲染进程、也就是我们常说的浏览器内核,
这可以避免单个 page crash 影响整个浏览器。

五、浏览器内核的多线程

浏览器内核就是浏览器渲染进程,从接收下载文件后再到呈现整个页面的过程,
由浏览器渲染进程负责。浏览器内核是多线程的,在内核控制下各线程相互配合以保持同步,一个浏览器通常由以下常驻线程组成:
GUI 渲染线程
定时触发器线程
事件触发线程
异步http请求线程
JavaScript 引擎线程

1.GUI渲染线程
GUI 渲染线程负责渲染浏览器界面 HTML 元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。

回流(重排):回流(reflow):也叫做重排。当元素的尺寸或者位置发生了变化,就需要重新计算渲染树,
这就是回流,比如元素的宽高、位置,浏览器会重新渲染页面,称为回流,又叫重排(layout)
重绘:当元素样式的改变不影响页面布局时,比如元素的颜色,浏览器将对元素进行的更新,称之为重绘。
当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制
解析HTML,生成DOM树,解析CSS,生成CSSOM树
将DOM树和CSSOM树结合,生成渲染树(Render Tree)
Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
Display:将像素发送给GPU,展示在页面上

关系:回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。
DOM 样式发生了变化,但没有影响到页面布局时,会触发重绘,而不会触发回流。
重绘由于 DOM 位置信息不需要更新,省去了布局过程,因而性能上优于回流

2.定时触发器线程
浏览器定时计数器并不是由 JavaScript 引擎计数的, 因为 JavaScript 引擎是单线程的,
如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案。

3.事件触发线程
当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。
这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,
但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。

4.异步http请求线程
在XMLHttpRequest在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,
如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理。

5.Javascript引擎线程
Javascript 引擎,也可以称为JS内核,主要负责处理 Javascript 脚本程序,例如V8引擎。
Javascript 引擎线程理所当然是负责解析 Javascript 脚本,运行代码。

由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和UI线程同时运行),
那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,
浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系,当 JavaScript 引擎执行时 GUI 线程会被挂起,
GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。

6、JavaScript 为何设计成单线程

从上面我们了解到 JavaScript 的执行是单线程的,也就是说,同一个时间只能做一件事。
那么,为什么 JavaScript 不设计成多个线程呢?这样不是效率更高?
作为浏览器脚本语言, JavaScript 的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,
否则会带来很复杂的同步问题。比如,假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,
另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生, JavaScript 就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

七、WebWorker 多线程?

Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面
那么既然 JavaScript 本身被设计为单线程,为何还会有像 WebWorker 这样的多线程 API 呢?
我们来看一下 WebWorker 的核心特点就明白了:

创建 Worker 时, JS 引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
JS 引擎线程与 worker 线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)
所以 WebWorker 并不违背 JS引擎是单线程的 这一初衷,其主要用途是用来减轻cpu密集型计算类逻辑的负担。

浏览器中的事件循环

JavaScript是单线程脚本语言,同一时间只能做一件事
而JS有很多异步操作 ajax setTimeout setInterval 事件绑定等
我们需要多线程的浏览器帮忙 将这些异步、事件、定时器触发的回调加入到任务队列中,等待js调用
而 js 循环的从任务队列中调用方法,我们就称之为事件循环。

任务队列的分类
任务队列分为两种,
一种叫宏任务(macrotask),
一种叫微任务(microtask ),这也是本文的重点。

接下来看看都有哪些属于宏任务,哪些属于微任务。

宏任务:script( 整体代码)、setTimeout、setInterval、I/O(http请求)、UI 渲染、requestAnimationFrame
微任务:Promise.then()、catch finally ,MutationObserver(监听dom的更改)

(1)js是单线程的,但是分同步异步
(2)微任务和宏任务皆为异步任务,它们都属于一个队列
(3)宏任务一般是:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
(4)微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
(5)先执行同步再执行异步,异步遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值