浏览器是如何调度进程和线程以及事件循环机制

进程和线程的概念:

进程:如下图所示,我们打开任务管理器,“进程”选项下我们可以看到钉钉,Foxmail,Chrome浏览器这些应用,这些应用都至少包含一个进程。

当我们关闭一个应用的时候,相应的该应用下的进程会被杀掉,内存被释放。

 

线程:和进程是父子关系,有进程才有线程。一个进程可以包含多个线程。多线程是指一个进程中可以同时运行多个不同的线程来执行不同的任务。

Chrome浏览器的多进程架构:

Chrome浏览器包括的进程:

  • 浏览器进程:最核心的进程,负责管理各个标签页的创建和销毁、页面显示和功能(前进,后退,收藏等)、网络资源的管理,下载等。
  • 插件进程:负责每个第三方插件的使用。
  • GPU进程:负责3D绘制和硬件加速。
  • 渲染进程:浏览器会为每个窗口分配一个渲染进程、也就是我们常说的浏览器内核,从接收下载文件后再到呈现整个页面的过程,由浏览器渲染进程负责。

浏览器内核的多线程(渲染进程的多线程):

浏览器内核(渲染进程)的多线程包括:

  • GUI 渲染线程(UI线程):负责渲染浏览器界面 HTML 元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
  • 定时触发器线程:浏览器定时计数器并不是由 JavaScript 引擎计数的, 因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案。
  • 事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
  • 异步http请求线程:XMLHttpRequest在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理。
  • JavaScript 引擎线程Javascript 引擎,也可以称为JS内核,主要负责处理 Javascript 脚本程序,例如V8引擎。Javascript 引擎线程理所当然是负责解析 Javascript 脚本,运行代码。

其中,“GUI 渲染线程”和“JavaScript 引擎线程”是互斥的,当JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。

JavaScript引擎与事件循环(event loop):

大家都知道JavaScript的执行是单线程的,那么它怎么做到不阻塞呢?比如我有个js页面需要解析执行,其中:

同步任务:所有同步任务按照执行顺序加入执行栈中依次执行。(没毛病)

异步任务浏览器内核(渲染进程)会开启异步请求线程和js引擎线程一起并行执行,等异步任务返回结果后,js会将该异步事件放入一个任务队列中,等待执行栈中的同步任务执行完之后再去拿事件队列中排在第一的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是“事件循环(Event Loop)”。

其中,异步任务又分宏任务(macro task)微任务(micro task)

以下事件属于宏任务:

  • setInterval()
  • setTimeout()
  • script(JS 整体代码)、setImmediate、I/O、事件、网络请求(ajax)

以下事件属于微任务

  • new Promise()
  • new MutaionObserver()

所以,前面提到的任务队列又分为宏任务队列微任务队列(每一个宏任务下包含了一组微任务。)。

执行优先顺序:当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。

JavaScript 为何设计成单线程:

举个例子:假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了避免复杂性,从一诞生, JavaScript 就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

 

 完。

如有错误,敬请指出,多谢!

 

posted @ 2020-10-15 15:18  redRunZhy  阅读(197)  评论(0编辑  收藏  举报