浏览器多线程和js单线程

1、什么是进程和线程

在涉及浏览器多线程和js单线程以前,咱们先铺垫一下前置概念:javascript

一、进程(process)

进程和线程都是操做系统的概念。html

进程是应用程序的执行实例,每一个进程是由私有的虚拟地址空间、代码、数据和其它各类系统资源组成,即进程是操做系统进行资源分配和独立运行的最小单元。java

当咱们启动一个应用,计算机会至少建立一个进程,cpu会为进程分配一部份内存,应用的全部状态都会保存在这块内存中,应用也许还会建立多个线程来辅助工做,这些线程能够共享这部份内存中的数据。若是应用关闭,进程会被终结,操做系统会释放相关内存。ios

mac电脑能够在活动监视器中查看启动的进程数: web

活动监视器

二、线程(thread)

  • 进程内部的一个执行单元,是被系统独立调度和分派的基本单位。系统建立好进程后,实际上就启动执行了该进程的主执行线程ajax

  • 进程就像是一个有边界的生产厂间,而线程就像是厂间内的一个个员工,能够本身作本身的事情,也能够相互配合作同一件事情,因此一个进程能够建立多个线程。算法

  • 线程本身不须要系统从新分配资源,它与同属一个进程的其它线程共享当前进程所拥有的所有资源。 PS: 进程之间是不共享资源和地址空间的,因此不会存在太多的安全问题,而因为多个线程共享着相同的地址空间和资源,因此会存在线程之间有可能会恶意修改或者获取非受权数据等复杂的安全问题。json

而如今通用叫法单线程与多线程,都是指在一个进程内的单和多。axios

若是对进程及线程的理解还存在疑惑,能够参考下述文章👇 www.ruanyifeng.com/blog/2013/0…浏览器

关于单核处理器、多核处理器、多处理器是怎么处理进程和线程的,能够参考下述文章👇 blog.csdn.net/alinshen/ar… jsonliangyoujun.iteye.com/blog/235827…

2、浏览器的多进程

其实若是要开发一个浏览器,它能够是单进程多线程的应用,也能够是使用 IPC 通讯的多进程应用。

不一样浏览器采用了不一样的架构模式,这里我们只研究以Chrome为表明的浏览器:

Chrome 采用多进程架构

Chrome 的不一样进程

每打开一个tab页,就至关于于建立了一个独立的浏览器进程,这一点从上面的图中能够看出,可是也不是绝对的,它也有本身的优化机制,有的进程可能会被合并。

一、Chrome 的主要进程及其职责

  • Browser Process 浏览器的主进程(负责协调、主控) (1)负责包括地址栏,书签栏,前进后退按钮等部分的工做 (2)负责处理浏览器的一些不可见的底层操做,好比网络请求和文件访问 (3)负责各个页面的管理,建立和销毁其余进程
  • Renderer Process 负责一个 tab 内关于网页呈现的全部事情,页面渲染,脚本执行,事件处理等
  • Plugin Process 负责控制一个网页用到的全部插件,如 flash 每种类型的插件对应一个进程,仅当使用该插件时才建立
  • GPU Process 负责处理 GPU 相关的任务

不一样进程负责的浏览器区域示意图

Chrome 还为咱们提供了「任务管理器」,供咱们方便的查看当前浏览器中运行的全部进程及每一个进程占用的系统资源,双击还能够查看更多类别信息。

经过「页面右上角的三个点点点 — 更多工具 — 任务管理器」便可打开相关面板。

任务管理器

二、Chrome 多进程架构的优缺点

优势: (1)某一渲染进程出问题不会影响其余进程 (2)更为安全,在系统层面上限定了不一样进程的权限

缺点: (1)因为不一样进程间的内存不共享,不一样进程的内存经常须要包含相同的内容。为了节省内存,Chrome 限制了最多的进程数,最大进程数量由设备的内存和 CPU 能力决定,当达到这一限制时,新打开的 Tab 会共用以前同一个站点的渲染进程。

Chrome 把浏览器不一样程序的功能看作服务,这些服务能够方便的分割为不一样的进程或者合并为一个进程。

以 Broswer Process 为例,若是 Chrome 运行在强大的硬件上,它会分割不一样的服务到不一样的进程,这样 Chrome 总体的运行会更加稳定,可是若是 Chrome 运行在资源贫瘠的设备上,这些服务又会合并到同一个进程中运行,这样能够节省内存。

3、浏览器的多线程

对咱们fe来说,最重要的是Renderer Process下的多线程,就是咱们常说的浏览器内核。

Chrome浏览器为每一个tab页面单独启用进程,所以每一个tab网页都有由其独立的渲染引擎实例

浏览器内核是多线程,在内核控制下各线程相互配合以保持同步,一个浏览器一般由如下常驻线程组成:

一、GUI 渲染线程

负责渲染浏览器界面HTML元素,当界面须要重绘(Repaint)或因为某种操做引起回流(reflow)时,该线程就会执行。在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了.

ps:重排和重绘的区别,有兴趣的话,请看👇的连接 juejin.im/entry/582f1…

二、JavaScript引擎线程

JS内核,负责处理Javascript脚本程序。 一直等待着任务队列中任务的到来,而后解析Javascript脚本,运行代码。一个Tab页(renderer进程)中不管何时都只有一个JS线程在运行JS程序。

ps: GUI渲染线程与JS引擎线程是互斥的,因此若是JS执行的时间过长,这样就会形成页面的渲染不连贯,致使页面渲染加载阻塞。

三、定时触发器线程

  • 定时器setInterval与setTimeout所在线程
  • 浏览器定时计数器并非由JavaScript引擎计数的 由于JavaScript引擎是单线程的, 若是处于阻塞线程状态就会影响记计时的准确, 所以经过单独线程来计时并触发定时是更为合理的方案。

四、事件触发线程

  • 用来控制事件轮询,JS引擎本身忙不过来,须要浏览器另开线程协助
  • 当JS引擎执行代码块如鼠标点击、AJAX异步请求等,会将对应任务添加到事件触发线程中
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理任务队列的队尾,等待JS引擎的处理
  • 因为JS的单线程关系,因此这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

五、异步http请求线程

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

let xhr = new XMLHttpRequest();   // 不兼容ie6及更低,建立ajax实例
  xhr.open('get',`json/banner.json?_${Math.random()}`); //打开请求:发送请求前的一些配置项,get有缓存,因此要加随机数,post不用
  xhr.onreadystatechange = ()=>{
     // 事件监听,ajax状态改变事件,基于这个事件能够获取服务器返回的响应头主体内容(响应头先回来)
     // 从这步开始,当前ajax任务开始,若是是同步的,后续代码不执行,要等到ajax状态成功后再执行,若是是异步的,不会
      if(xhr.readyState === 4 && /^(2|3)\d{2}$/.test(xhr.status)){
          //readyState 请求状态 // status 返回状态
          let data = JSON.parse(xhr.responseText);  // 获取响应主体的内容
      }
  };
  xhr.send(null); // 发送 ajax (括号中传递的内容就是请求主体的内容)
复制代码

这是一个简单的XMLHttpRequest请求的四个步骤,基于XMLHttpRequest的表明产品$.ajax,axios等

3、Js为何要是单线程?

js的单线程和它的用途有关,做为浏览器脚本语言,它主要是用来处理页面中用户的交互,以及操做DOM树、CSS样式树来给用户呈现一份动态而丰富的交互体验和服务器逻辑的交互处理。

若是JavaScript是多线程的方式来操做这些UI DOM,则可能出现UI操做的冲突;

若是Javascript是多线程的话,在多线程的交互下,处于UI中的DOM节点就可能成为一个临界资源,假设存在两个线程同时操做一个DOM,一个负责修改一个负责删除,那么这个时候就须要浏览器来裁决如何生效哪一个线程的执行结果。

固然咱们能够经过锁来解决上面的问题。但为了不由于引入了锁而带来更大的复杂性,Javascript在最初就选择了单线程执行

这也解释了为何GUI线程和JS引擎是互斥的。

当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时当即被执行。

为了多核CPU的计算能力,HTML5提出Web Worker标准,容许JavaScript脚本建立多个线程,可是子线程彻底受主线程控制,且不得操做DOM。因此,这个新标准并无改变JavaScript单线程的本质。

4、Js的单线程会带来什么问题?怎么处理的?

当调用栈中的函数调用须要花费咱们很是多的时间,会发生什么?

好比正在运行一个复杂的图像转换的算法

当调用栈有函数在执行,浏览器就不能作任何事了 —— 它被阻塞了。这意味着浏览器不能渲染页面,不能运行任何其它的代码,它就这样被卡住了。那么问题来了 —— 你的应用再也不高效和使人满意了。

一旦你的浏览器开始在调用栈运行不少不少的任务,它就颇有可能会长时间得不到响应。在这一点上,大多数的浏览器会采起抛出错误的解决方案,询问你是否要终止这个页面:

但这会毁了你的用户体验

js开发的时候有一个很重要概念:

以后 的代码并不必定会在 如今 的代码执行以后执行。换句话说,在定义中不能 如今 马上完成的任务将会异步执行,这意味着可能不会像你认为的那样发生上面所说的阻塞问题。

谁会告诉 JS 引擎去执行你的程序?事实上,JS 引擎不是单独运行的 —— 它运行在一个宿主环境中,对于大多数开发者来讲就是典型的浏览器和 Node.js。实际上,现在,JavaScript 被应用到了从机器人到灯泡的各类设备上。每一个设备都表明了一种不一样类型的 JS 引擎的宿主环境。

全部的环境都有一个共同点,就是都拥有一个事件循环内置机制,它随着时间的推移每次都去调用 JS 引擎去处理程序中多个块的执行。

这意味着 JS 引擎只是任意的 JS 代码按需执行的环境。是它周围的环境来调度这些事件(JS 代码执行)。,其余周围环境就是指的在同一进程下的不一样的线程,好比事件触发线程、定时器线程等

因此,好比当你的 JavaScript 程序发出了一个 Ajax 请求去服务器获取数据,你在一个函数(回调)中写了 “response” 代码,而后 JS 引擎就会告诉宿主环境: “嘿,我如今要暂停执行了,可是当你完成了这个网络请求,而且获取到数据的时候,请回来调用这个函数。”

而后浏览器设置对网络响应的监听,当它有东西返回给你的时候,它将会把回调函数插入到事件循环队列里而后执行。

浏览器种的EventLoop

若是你想详细的了解这方面的内容,👇的连接: web.jobbole.com/95613/

相关文章
相关标签/搜索