18.1 进程与线程
涉及面试题:进程与线程的区别?JS
单线程带来的好处?
- 线程和进程都是
CPU
工作时间片的一个描述 - 进程描述了
CPU
在运行指令及加载和保存上下文所需的时间,放在应用上来说就代表了一个程序 - 线程是进程中的更小单位,描述了执行一段指令所需的时间
- 进程是资源分配的最小单位,线程是CPU调度的最小单位
做个简单的比喻:进程=火车,线程=车厢
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
- 进程可以拓展到多机,进程最适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
- 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-“互斥锁”
- 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
把这些概念拿到浏览器中来说,当你打开一个
Tab
页时,其实就是创建了一个进程,一个进程中可以有多个线程,比如渲染线程、JS
引擎线程、HTTP
请求线程等等。当你发起一个请求时,其实就是创建了一个线程,当请求结束后,该线程可能就会被销毁
浏览器中的线程分了以下几类:
JS
线程UI
线程event
线程- 定时器线程
http
线程
JS
引擎线程和渲染线程,在JS
运行的时候可能会阻止UI
渲染,这说明了两个线程是互斥的。这其中的原因是因为JS
可以修改DOM
,如果在JS
执行的时候UI
线程还在工作,就可能导致不能安全的渲染UI
。这其实也是一个单线程的好处,得益于JS
是单线程运行的,可以达到节省内存,节约上下文切换时间,没有锁的问题的好处
18.2 进程间的通信方式
-
无名管道(pipe):半双工通信,单向流动,通常父子进程使用
-
高级管道(popen):另一进程当做新进程在当前进程启动,把他当做当前进程的子进程。
-
有名管道(named pipe):允许无亲缘关系的进程通信
-
消息队列(message queue):消息链表,解决了管道传递信息少,缓冲区大小受限问题
-
信号量(semphore):一种计数器,常用于加锁,信号量常用的事pv操作,是一种不同进程间同步的手段
-
信号(sinal):通知某进程,某事件已经发生
-
共享内存(shared memory): 映射一些其他进程访问的内存,一般与信号量配合使用,实现进程同步与通信。
-
套接字(socket):用与不同机器之间的进程通信
18.3 协程
协程,英文Coroutines,是一种基于线程之上,但又比线程更加轻量级的存在,这种由程序员自己写程序来管理的轻量级线程叫做『用户空间线程』,具有对内核来说不可见的特性。
因为是自主开辟的异步任务。一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
协程的目的
在传统的J2EE系统中都是基于每个请求占用一个线程去完成完整的业务逻辑(包括事务)。所以系统的吞吐能力取决于每个线程的操作耗时。如果遇到很耗时的I/O行为,则整个系统的吞吐立刻下降,因为这个时候线程一直处于阻塞状态,如果线程很多的时候,会存在很多线程处于空闲状态(等待该线程执行完才能执行),造成了资源应用不彻底。
最常见的例子就是JDBC(它是同步阻塞的),这也是为什么很多人都说数据库是瓶颈的原因。这里的耗时其实是让CPU一直在等待I/O返回,说白了线程根本没有利用CPU去做运算,而是处于空转状态。而另外过多的线程,也会带来更多的ContextSwitch开销。
对于上述问题,现阶段行业里的比较流行的解决方案之一就是单线程加上异步回调。其代表派是node.js以及Java里的新秀Vert.x。
协程的目的就是当出现长时间的I/O操作时,通过让出目前的协程调度,执行下一个任务的方式,来消除ContextSwitch上的开销。