浏览器多进程解析

658 阅读5分钟

浏览器是多进程的

  • 浏览器之所以能运行,是因为系统给它的进程分配了资源(cpu、内存)
  • 每开一个标签页,就相当于创建了一个独立的浏览器进程。(有的时候会将多个标签页合并成一个进程)

浏览器包含哪些进程

1、Broswer进程(主线程):

  • 负责浏览器界面的显示、用户交互(如前进、后退)
  • 负责各个页面的管理,创建和销毁其他进程
  • 负责将渲染进程得到的内存中的Bitmap(位图)绘制到用户界面上
  • 网络资源的管理、下载等

2、第三方插件进程:

  • 每种类型的插件对应一个进程,仅当使用该插件时才创建

3、GPU进程:

  • 最多一个,用于3D绘制等

4、浏览器渲染进程(浏览器内核):

  • 该进程内部是多线程
  • 每个tab页面一个进程,互不影响(打开浏览器时,就会看到两个进程:主进程+Tab页的渲染进程)
  • 主要负责页面渲染、脚本执行、时间处理

浏览器渲染进程 -- 浏览器内核

多线程:

  • GUI渲染线程
    • 负责渲染页面,解析HTML、CSS文件,构建DOM树和CSS树,将DOM树和CSS树合并成render树
    • 将渲染结果:位图交给主进程(Broswer进程)显示
    • 当界面需要重绘(repaint)或某种操作导致回流(reflow)时,该线程就会执行。
    • GUI渲染线程与JS引擎线程是互斥的,当JS引擎线程执行时,该线程就会被挂起,GUI更新会保存在一个队列中,等JS引擎空闲时再执行。
  • JavaScript引擎线程(主线程)
    • 也成为JS内核,负责解析处理js脚本,运行代码。(例如V8引擎)
    • 一个Tab页无论什么时候都只有一个JS线程在运行JS程序。
  • 事件触发线程
    • 归属于浏览器而不是JS引擎,用来控制事件循环(因为JS引擎自己都忙不过来,需要浏览器另开线程辅助)
    • 当有事件时,就会添加到事件线程中,比如当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
    • 管理任务队列:当满足触发条件时,事件触发线程就会将事件监听函数放置到任务队列的队尾,等待主线程空闲时调用。
  • 定时触发器线程
    • 传说中的setInterval与setTimeout所在线程
    • 负责计时,计时完毕后,将事件监听函数添加到任务队列里
    • 因为JS引擎是单线程的,如果由JS引擎计时,有可能JS引擎发生堵塞,导致计时不准确
  • 异步HTTP请求线程
    • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

浏览器内核中线程之间的关系

  • GUI渲染线程与JS引擎线程互斥
  • JS阻塞页面加载
    • 从互斥的关系就可推导出,如果JS执行时间太长就会阻塞页面的渲染

浏览器渲染流程

渲染

如何解决JS单线程导致的问题

当JS执行时间过长会阻塞页面,所以后来HTML5中支持了WebWorker

  • WebWorker
    • 当有非常耗时的工作时,比如计算等,就可以创建一个Worker线程
    • 用构造函数创建Worker线程,JS引擎会向浏览器申请开一个子线程,受主线程控制,不能操作DOM
    • Worker线程相当于浏览器为JS引擎开设的外挂,专门用来解决那些大量计算问题
    • JS主线程与Worker线程的通信是通过postMessage API,需要通过序列化对象来与线程交互特定的数据。当计算出结果后,将结果通信给主线程即可。
<!DOCTYPE html>
<html>
<body>

<p>Count numbers: <output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button> 开启worker线程
<button onclick="stopWorker()">Stop Worker</button> 停止worker线程
<br /><br />

<script>
var w;

function startWorker()
{
if(typeof(Worker)!=="undefined") //浏览器是否支持
{
  if(typeof(w)=="undefined")
    {
    w=new Worker("demo_workers.js");//参数就是Worker线程要执行的代码文件
    }
  w.onmessage = function (event) {
    document.getElementById("result").innerHTML=event.data;
  }; 
  ## w是在主线程下的变量,onmessage监听Worker线程,
  ## 接收Worker线程在"demo_workers.js"文件通过
  ## postMessage()传回消息
}
else
{
document.getElementById("result").innerHTML="Sorry, your browser
 does not support Web Workers...";
}
}

function stopWorker()
{
w.terminate();  //停止worker线程
}
</script>

</body>
</html>

"demo_workers.js"文件

var i=0;

function timedCount()
{
i=i+1;
postMessage(i); //它用于向JS主线程传回一段消息
setTimeout("timedCount()",500);
}

timedCount();

WebWorker与SharedWorker的区别

  • WebWorker是属于一个Tab页面下的线程,不会和其他页面的Render进程(浏览器内核进程)共享
  • SharedWorker是浏览器所有页面共享的,是浏览器单独创建的进程。不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用

宏任务与微任务

顺序:宏任务 > 所有微任务 > GUI渲染线程 > 下一个宏任务

参考资料

资料