多进程多线程浏览器(比如 Chrome)

线程:是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元。
多线程:是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
进程:是应用程序的执行实例,每一个进程都是由私有的虚拟地址空间、代码、数据和其它系统资源所组成。
多进程:多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。
进程在运行过程中能够申请创建和使用系统资源(如独立的内存区域等),这些资源也会随着进程的终止而被销毁。而线程则是进程内的一个独立执行单元,在不同的线程之间是可以共享进程资源的,所以在多线程的情况下,需要特别注意对临界资源的访问控制。在系统创建进程之后就开始启动执行进程的主线程,而进程的生命周期和这个主线程的生命周期一致,主线程的退出也就意味着进程的终止和销毁。主线程是由系统进程所创建的,同时用户也可以自主创建其它线程,这一系列的线程都会并发地运行于同一个进程中。
Chrome浏览器使用多个进程来隔离不同的网页。因此在Chrome中打开一个网页相当于起了一个进程。把所有网页都放进一个进程的浏览器面临在健壮性,响应速度,安全性方面的挑战。因为如果浏览器中的一个tab网页崩溃的话,将会导致其他被打开的网页应用。另外相对于线程,进程之间是不共享资源和地址空间的,所以不会存在太多的安全问题,而由于多个线程共享着相同的地址空间和资源,所以会存在线程之间有可能会恶意修改或者获取非授权数据等复杂的安全问题。
注意: 在这里浏览器应该也有自己的优化机制,有时候打开多个tab页后,可以在Chrome任务管理器中看到,有些进程被合并了(譬如打开多个空白标签页后,会发现多个空白标签页被合并成了一个进程),所以每一个Tab标签对应一个进程并不一定是绝对的。

除了浏览器的标签页进程之外,浏览器还有一些其他进程来辅助支撑标签页的进程,如下:
① Browser进程:浏览器的主进程(负责协调、主控),只有一个。作用有

负责浏览器界面显示,与用户交互。如前进,后退等
负责各个页面的管理,创建和销毁其他进程
网络资源的管理,下载等
② 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
③ GPU进程:最多一个,用于3D绘制等
④ 浏览器渲染进程(浏览器内核),Renderer进程,内部是多线程的,也就是我们每个标签页所拥有的进程,简单来说浏览器内核是通过取得页面内容、整理信息(应用CSS)、计算和组合最终输出可视化的图像结果,通常也被称为渲染引擎。从上面我们可以知道,Chrome浏览器为每个tab页面单独启用进程,因此每个tab网页都有由其独立的渲染引擎实例。该进程下面拥有着多个线程,靠着这些现成共同完成渲染任务。那么这些线程是什么呢,如下:
1.js引擎(JS内核)线程(js引擎有多个线程,一个主线程,其它的后台配合主线程)
作用:
A:主要负责处理Javascript脚本程序,例如V8引擎。Javascript引擎线程理所当然是负责解析Javascrip脚本,运行代码。
B:等待任务队列的任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS引擎在运行JS程序
2.GUI渲染线程
作用:
A:负责渲染浏览器界面,包括解析HTML、CSS、构建DOM树、Render树、布局与绘制等
B:当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
3.事件触发线程
作用:听起来像JS的执行,但是其实归属于浏览器,而不是JS引擎。控制交互,响应用户,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
4.http请求线程
作用:ajax请求等,在XMLHttpRequest在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理。
5.定时触发器线程
作用:setTimeout和setInteval,浏览器定时计数器并不是由JavaScript引擎计数的, 因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案。
在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了,GUI更新会被保存在一个队列中等到引擎线程空闲时立即被执行。如果JS引擎正在进行CPU密集型计算,那么JS引擎将会阻塞,长时间不空闲,导致渲染进程一直不能执行渲染,页面就会看起来卡顿卡顿的,渲染不连贯,所以,要尽量避免JS执行时间过长。如果需要进行一些高耗时的计算时,可以单独开启一个WebWorker线程,这样不管这个WebWorker子线程怎么密集计算、怎么阻塞,都不会影响JS引擎主线程,只需要等计算结束,将结果通过postMessage传输给主线程就可以了。另外,还有个东西叫 SharedWorker,与WebWorker在概念上所不同。WebWorker 只属于某一个页面,不会和其他标签页的Renderer进程共享,WebWorker是属于Renderer进程创建的进程。SharedWorker 是由浏览器单独创建的进程来运行的JS程序,它被所有的Renderer进程所共享,在浏览器中,最多只能存在一个SharedWorker进程。SharedWorker由进程管理,WebWorker是某一个Renderer进程下的线程.

为什么Javascript要是单线程的?
这是因为Javascript这门脚本语言诞生的使命所致:JavaScript为处理页面中用户的交互,以及操作DOM树、CSS样式树来给用户呈现一份动态而丰富的交互体验和服务器逻辑的交互处理。如果JavaScript是多线程的方式来操作这些UI DOM,则可能出现UI操作的冲突; 如果Javascript是多线程的话,在多线程的交互下,处于UI中的DOM节点就可能成为一个临界资源,假设存在两个线程同时操作一个DOM,一个负责修改一个负责删除,那么这个时候就需要浏览器来裁决如何生效哪个线程的执行结果。当然我们可以通过锁来解决上面的问题。但为了避免因为引入了锁而带来更大的复杂性,Javascript在最初就选择了单线程执行。

ps:web worker也只是允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。

但是如果单线程,任务都需要排队。排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

同步任务和异步任务
同步任务:在主线程排队支持的任务,前一个任务执行完毕后,执行后一个任务,形成一个执行栈,线程执行时在内存形成的空间为栈,进程形成堆结构,这是内存的结构。执行栈可以实现函数的层层调用。

异步任务会被主线程挂起,不会进入主线程,而是进入消息队列,而且必须指定回调函数,只有消息队列通知主线程,并且执行栈为空时,该消息对应的任务才会进入执行栈获得执行的机会。

主线程
主线程执行: 【js的运行机制】
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值