进程和线程
进程
- 启动一个应用程序时计算机会创建一个到多个进程(打开一个浏览器,启动一个进程,打开多个浏览器标签页,对应启动多个进程);
- CPU 会为进程分配一部分内存,有自己的地址空间,进程之间相互独立;
- 应用的所有数据/状态都会保存在这块内存中;
总结:进程是操作系统【
资源调度】的基本单位
线程
线程是CPU【任务调度】的基本单位,它可以和同一进程下的其他线程共享全部资源
联系
- 一个进程包含一个或多个线程,但线程只能存在于一个进程中
- 同一个进程下的不同线程之间共享该进程下的空间资源(内存空间)
区别
- 根本区别:
- 进程是操作系统
资源调度的基本单位 - 线程是CPU
任务调度执行的基本单位
- 进程是操作系统
- 切换开销:
- 进程都有自己的独立数据空间,程序之间的
切换开销大; - 线程也有自己的运行栈和程序计数器,线程间的
切换开销较小。
- 进程都有自己的独立数据空间,程序之间的
- 共享和同步:
- 进程之间空间、资源相互独立,所以
共享复杂,需要用IPC(进程间通信),但是同步简单 - 线程共享所属进程的资源,因此
共享简单,但是同步复杂,需要用加锁等措施。
- 进程之间空间、资源相互独立,所以
设计线程的原因
操作系统中有两个重要概念:并发和隔离
- 并发:提高硬件利用率,进程的上下文切换比线程的上下文切换效率低,所以线程可以提高并发的效率
- 隔离:计算机的资源是共享的,当程序发生奔溃时,需要保证这些资源要被回收,进程的资源是独立的,奔溃时不会影响其他程 序的进行,线程资源是共享的,奔溃时整个进程也会奔溃
进程自身可以完成隔离,但是需要线程来完成并发(比如浏览器遇到setTimeout,不让渲染线程做,而是让定时器线程做),原因是线程间的切换开销比进程间的切换开销小
如何解决因为线程共享资源引发的冲突?
互斥锁:防止多个线程同时读写某一块内存区域(适用一个进程包含一个线程的情况) 打个比方:一车间一人,先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去信号量:用来保证多个线程不会互相冲突(适用一个进程包含多个线程的情况) 打个比方:一车间多人,门口放一串钥匙串,进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了
浏览器的多进程
浏览器的主要进程及其职责
- 浏览器进程:浏览器的主进程(
负责协调、主控)
- 负责包括地址栏,书签栏,前进后退按钮等部分的工作
- 负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问
- 负责各个页面的管理,创建和销毁其他进程
- 渲染进程:负责一个 tab 内关于
网页呈现的所有事情,页面渲染,脚本执行,事件处理等 - 插件进程:负责
管理所有插件,如 flash,每种类型的插件对应一个进程,仅当使用该插件时才创建 - GPU进程:负责处理 GPU 相关的任务 另外,以 Chrome 为例,每打开一个tab页,就相当于创建了一个独立的浏览器进程(当然有可能会启动其他进程辅助工作)
浏览器多进程的优缺点
- 优点: (1)某一渲染进程出问题不会影响其他进程 (2)更为安全,在系统层面上限定了不同进程的权限
- 缺点:
由于不同进程间的内存不共享,不同进程的内存常常需要
包含相同的内容。为了节省内存,Chrome 限制了最多的进程数,最大进程数量由设备的内存和 CPU 能力决定,当达到这一限制时,新打开的 Tab 会共用之前同一个站点的渲染进程。
Chrome 把浏览器不同程序的功能看做服务,这些服务可以方便的分割为不同的进程或者合并为一个进程。
渲染进程 —— 多线程
对于前端而言,浏览器多个进程中最多关注的是渲染进程:
- 每个tab页对应一个渲染进程
- 每个渲染进程负责HTML、,css,js等文件的解析,执行和渲染,以及事件处理等
- 每个渲染进程由多个线程构成:(1)GUI线程;(2)JS引擎线程;(3)事件触发线程;(4)定时器线程;(5)异步的http网络请求线程
1. GUI渲染线程
负责页面渲染,这是主任务。没有完成页面渲染用户看什么[笑哭]
具体渲染过程见 浏览器系列 -- 渲染原理及过程
2. JavaScript 引擎线程
负责解析Javascript脚本,运行代码。
比如帮助页面和用户之间交互的 DOM 操作,这也是主任务
对于下面三个进程我的理解是:本来这些事应该由 JS 引擎线程干的,但由于 JS 语言是单线程的,而下面三种处理又比较费时,不想浪费时间让队伍卡住,所以浏览器把这些“分担”给其他的线程去做,保证 JS 引擎线程的运行畅通
问:为什么 JS 引擎是单线程的?
因为 JS 本身作为浏览器脚本语言,它操作 DOM 树、CSS 样式树来处理页面中用户的交互,如果是多线程的将会 UI 操作的冲突。单线程就不会。
问:单线程遇到异步任务怎么处理
- 同步任务交给 JS 引擎线程处理;
- 异步任务对应交给定时器线程、事件触发线程、异步 http 请求线程
3. 定时触发器线程
浏览器定时计数器并不是由 JavaScript 引擎计数的, 因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时
4. 事件触发线程
当一个 DOM 事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理
DOM 事件有很多,有输入事件(包含键盘事件)、鼠标事件、点击事件、加载事件,详见 JavaScript HTML DOM 事件实例 参考手册
5. 异步http请求线程
XMLHttpRequest 请求会在浏览器中新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中
具体内容见 JavaScript系列 -- 异步编程(未完成)
另外注意:不同浏览器的内核实现有所差异,导致对网页编写语法的解释也有不同(JS引擎线程),因此同一网页在不同的内核的浏览器里的渲染结果也可能不同(GUI渲染线程),这就抛出了常见的 “浏览器兼容性问题”