什么是进程?
- 我们都知道,
CPU
是计算机的核心,承担所有的计算任务 - 官网说法,
进程
是CPU
资源分配的最小单位 - 字面意思就是进行中的程序,我将它理解为一个可以独立运行且拥有自己的资源空间的任务程序
-
进程
包括运行中的程序和程序所使用到的内存和系统资源 -
CPU
可以有很多进程,我们的电脑每打开一个软件就会产生一个或多个进程
,为什么电脑运行的软件多就会卡,是因为CPU
给每个进程
分配资源空间,但是一个CPU
一共就那么多资源,分出去越多,越卡,每个进程
之间是相互独立的,CPU
在运行一个进程
时,其他的进程处于非运行状态,CPU
使用 时间片轮转调度算法 来实现同时运行多个进程
什么是线程?
- 线程是
CPU调度的最小单位
- 线程是建立在进程的基础上的一次程序运行单位,通俗点解释线程就是程序中的一个执行流,
一个进程可以有多个线程
- 一个进程中只有一个执行流称作
单线程
,即程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行 - 一个进程中有多个执行流称作
多线程
,即在一个程序中可以同时运行多个不同的线程来执行不同的任务, 也就是说允许单个程序创建多个并行执行的线程来完成各自的任务
进程和线程的区别?
- 进程是操作系统分配
资源的最小单位
,线程是程序执行的最小单位
- 一个进程由一个或多个线程组成,线程可以理解为是一个进程中代码的不同执行路线
- 进程之间相互独立,但同一进程下的各个线程间
共享程序的内存空间
(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号) - 调度和切换:线程上下文切换比进程上下文切换要
快得多
多进程和多线程
-多进程
:多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。多进程带来的好处是明显的,比如大家可以在网易云听歌的同时打开编辑器敲代码,编辑器和网易云的进程之间不会相互干扰
-
多线程
:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务
浏览器
浏览器是多进程的
- 拿Chrome来说,我们每打开一个窗口就会产生一个进程,我们使用Chrome打开很多标签页不关,电脑会越来越卡,很耗CPU。
浏览器包含哪些进程?
- Browser进程(
浏览器主进程
)
1、浏览器的主进程(负责协调、主控),该进程只有一个
2、负责浏览器界面显示,与用户交互。如前进,后退等
3、负责各个页面的管理,创建和销毁其他进程
4、将渲染(Renderer)进程得到的内存中的Bitmap(位图),绘制到用户界面上
5、网络资源的管理,下载等 - 第三方插件进程
1、每种类型的插件对应一个进程,当使用该插件时才创建 - GPU进程
1、该进程也只有一个,用于3D绘制等等 - 渲染进程(
重要
)
1、即通常所说的浏览器内核(Renderer进程,内部是多线程)
2、每个Tab页面都有一个渲染进程,互不影响
3、主要作用为页面渲染,脚本执行,事件处理等
4、渲染进程包括
:GUI渲染线程(UI、DOM树、CSS规则树、Rendering Tree(渲染树
)...)
浏览器进程工作
1、Browser主进程 收到用户请求,首先需要获取页面内容(如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render渲染进程
2、Render渲染进程 的Renderer接口收到消息,简单解释后,交给渲染线程GUI,然后开始渲染
3、GUI渲染线程接收请求,加载网页并渲染网页,这其中可能需要 Browser主进程 获取资源和需要 GPU进程 来帮助渲染
4、当然可能会有JS线程操作DOM(这可能会造成回流并重绘)
5、最后Render渲染进程 将结果传递给 Browser主进程
6、Browser主进程 接收到结果并将结果绘制出来
为什么JavaScript是单线程?
- JavaScript语言的一大特点就是
单线程
,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。 - JavaScript作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准,如果设计多线程,JavaScript会变行很复杂(个人觉得会有很多线程锁的操作问题)。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征。
- 为了利用多核CPU的计算能力,HTML5提出
Web Worker
标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
任务队列【先进先出
】
- js单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
- 任务可以分成两种,一种是
同步任务(主线程任务)
,另一种是异步任务
。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)
的任务,只有"任务队列"
通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 - 任务队列分为
宏任务和微任务
,微任务优先宏任务。为什么优先?没有找到答案,个人认为主线程任务也是宏任务,宏任务执行完马上找微任务,微任务执行完再马上执行宏任务,这样行成一个事件循环
。 - 宏任务主要包含:
script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
。 - 微任务主要包含:
Promise、MutaionObserver、process.nextTick(Node.js 环境)
-
异步执行的运行机制
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
。
(2)主线程之外,还存在一个"任务队列"(task queue)
。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"
中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。先执行微任务再执行宏任务
。
(4)主线程不断重复
上面的第三步,不断的去检查任务队列是否有可执行的任务。
- 只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
事件和回调函数
- "任务队列"是一个
事件的队列(也可以理解成消息的队列)
,IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。 - "任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。
- 所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
- "任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要
执行栈一清空
,"任务队列"上第一位的事件就自动进入主线程。但是"定时器"功能
,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
Event Loop(事件循环,主线程任务全部执行完成再找任务队列是否有任务,如果有,先执行队列中的微任务再执行队列中的宏任务
)
- 是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用
异步的原理
。主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环
)。 - 在JavaScript中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)。
宏任务
- 主要包含:
script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
微任务
- 主要包含:
Promise、MutaionObserver、process.nextTick(Node.js 环境)
image.png
//宏任务和微任务例子:
console.log('script start');
setTimeout(function() {
console.log('setTimeout1');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
1、整体 script 作为第一个宏任务进入主线程,遇到 console.log,输出 script start。
2、遇到 setTimeout,其回调函数被分发到队列任务中的宏任务队列中。
3、遇到 Promise,其 then函数被分到到队列任务中的微任务队列中,记为 then1,之后又遇到了 then 函数,将其分到队列任务中的微任务队列中,记为 then2。
4、遇到 console.log,输出 script end。
5、此时的队列任务表示为: [ [ then1 , then2 ] , [ setTimeout ]], 再循环执行,输出 promise1 - promise2 - setTimeout,
- 整个输出流程为:script start - script end - promise1 - promise2 - setTimeout
- 小知识:
1、W3C在HTML中规定setTimeout的最小间隔时间是4毫秒
;setInterval的最小间隔时间是10毫秒
,如果没有设置时间,就自动按最小间隔时间。
2、需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。定时器的执行时间会大于指定的时间执行
,因为并不是指定的时间执行,而是等主线程完全执行成完后再执行指定的时间执行的定时器。
3、浏览器工作流程:构建DOM -> 构建CSSOM -> 构建渲染树 -> 布局 -> 绘制。