进程与线程

一个程序中至少有一个进程,而一个进程中至少有一个线程

  • 进程是运行中的程序,线程是进程内部的一个执行序列
  • 进程是资源分配的单元,线程是执行单元
  • 进程间切换代价大,线程间切换代价小
  • 进程拥有的资源多,线程拥有的资源少
  • 多个线程共享进程的资源

如:

工厂的资源 -> 系统分配的内存(独立的一块内存)

工厂之间的相互独立 -> 进程之间相互独立

多个工人协作完成任务 -> 多个线程在进程中协作完成任务

工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成

工人之间共享空间 -> 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)

浏览器是多进程的!!!

浏览器的进程:

  • Browser进程:浏览器的主进程(负责协调、主控),只有一个
    • 负责浏览器界面显示,与用户交互。如前进后退
    • 负责各个页面的管理,创建和销毁其他进程
    • 将Renderer进程得到的内存中的Bitmap,会知道用户界面上
    • 网络资源的管理,下载等
  • 第三方插件进程:每种类型的插件对应一个进程,仅当使用插件时才创建
  • GPU进程:最多一个,用于3D绘制等
  • 浏览器渲染进程(浏览器内核)(Renderer进程内部是多线程的):默认每个Tab页面一个进程,互不影响。
    • 页面渲染、脚本执行、事件处理

重点:浏览器内核(渲染进程)

渲染进程是多线程的

包含:

  • GUI渲染线程
    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行
  • JS引擎线程
    • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
    • JS引擎线程负责解析Javascript脚本,运行代码。
    • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
    • 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
  • 事件触发线程
    • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
    • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
    • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
    • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
  • 定时器触发线程
    • 传说中的setInterval与setTimeout所在线程
    • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
    • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
    • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
  • 异步http请求线程
    • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

单线程与多线程

单线程

所谓单线程(主线程),指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。

多线程

如浏览器的渲染进程:

  • GUI渲染线程
  • JS引擎线程
  • 事件触发线程
  • 定时器触发线程
  • 异步http请求线程

同步和异步

同步:如果在函数返回的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的

例如:

console.log('我是第一件事');
console.log('我是第二件事');

1.这两个函数都是同步的

2.这段代码是同步的

3.若有一个函数执行时间很长,后面的函数只能等待这个函数执行完才能执行(同步是阻塞的)

异步

先来看段代码:

console.log('我是第一件事');
setTimeout(function () {
  console.log('我突然有事,晚点再做第二件事');
},1000)
console.log('我是第三件事');

//我是第一件事
//我是第三件事
//我突然有事,晚点再做第二件事

这段代码实现的就是异步

1.执行console.log('我是第一件事')

2.setTimeout异步函数跳过

3.执行console.log('我是第三件事')

4.js引擎空闲1秒后将异步函数推入事件队列-->执行console.log('我突然有事,晚点再做第二件事')

补充:setTimeout和setInterval接受两个参数,第一个参数为函数。第二个为时间(毫秒),及js引擎空闲几秒后将其推入事件队列。

再来一个:

console.log(0,'第一');
for (let i = 0;i<3;i++){
    setTimeout(function(){
        console.log(i,'第三');
    },2000)
    console.log(i,'第二');
};

//0 "第一"
//0 "第二"
//1 "第二"
//2 "第二"
//两秒之后
//0 "第三"
//1 "第三"
//2 "第三"

补充:

  • 异步机制是浏览器的两个或两个以上的常驻线程共同完成的
  • 如:异步请求是由两个常驻线程:JS执行线程和事件触发线程共同完成的,JS的执行线程发起异步请求(这时浏览器会开一条新的HTTP请求线程来执行请求,这时JS的任务已完成,继续执行线程队列中剩下的其他任务),然后在未来的某一时刻事件触发线程监视到之前的发起的HTTP请求已完成,它就会把完成事件插入到JS执行队列的尾部等待JS处理。
  • 如:定时触发(settimeout和setinterval)是由浏览器的定时器线程执行的定时计数,然后在定时时间把定时处理函数的执行请求插入到JS执行队列的尾端

消息队列与事件循环

如上图所示:

  • 左边的栈存储的是同步任务,就是那些能立即执行、不耗时的任务,如变量和函数的初始化、事件的绑定等等那些不需要回调函数的操作都可归为这一类。
  • 右边的堆用来存储声明的变量、对象。
  • 下面的队列就是消息队列,一旦某个异步任务有了响应就会被推入队列中。如用户的点击事件、浏览器收到服务的响应和setTimeout中待执行的事件,每个异步任务都和回调函数相关联。
  • JS引擎线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空,然后读取消息队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务。

  • JS引擎线程从消息队列中读取任务是不断循环的,每次栈被清空后,都会在消息队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫事件循环。

以Ajax异步请求为例:

实例

最后来个大的:

setTimeout(function(){
    for(var i = 0; i < 100000000; i++){}
    console.log('timer a');
}, 0)

for(var j = 0; j < 5; j++){
    console.log(j);
}

setTimeout(function(){
    console.log('timer b');
}, 0)

function waitFiveSeconds(){
    var now = (new Date()).getTime();
    while(((new Date()).getTime() - now) < 5000){}
    console.log('finished waiting');
}

document.addEventListener('click', function(){
    console.log('click');
})

console.log('click begin');
waitFiveSeconds();

//0
//1
//2
//3
//4
//click begin
//finished waiting
//timer a
//timer b
//click

再说一下:setTimeout(fn,0);是js引擎空闲后立即插入队列,并不是立即执行

1.第一个setTimeout异步函数跳过

2.执行for循环输出0,1,2,3,4

3.第二个setTimeout异步函数跳过

4.执行console.log('click begin')输出 click begin

5.调用waitFiveSeconds()函数输出finished waiting

6.waitFiveSeconds()执行完后同步任务结束,js引擎空闲会一次将异步函数插入队列

7.输出timer a;timer b;click

参考文章1
参考文章2

内容来源于网络如有侵权请私信删除
你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!