在多数的情况下,我们谈论这几个概念时都是涉及到I/O操作时,当计算机在等待数据从磁盘或者其他存储设备(网络socket)到达用户进程所用空间时所涉及的几个概念。
我们认为首先CPU会发出一个I/O操作的通知,然后文件系统或其他会调用相关设备执行这些操作,最后当数据到达用户空间后发出一个中断的完成标志,于是在这个从CPU发出调用到收到完成标志的过程中就存在一个时间差。现在就有了两个重要的概念:完成标志与时间差。同步与异步是针对获取完成标志而言的,而阻塞与非阻塞是针对时间差而言的。
同步与异步:获取完成标志的方式。如果是采用轮询的方式监测I/O操作是否完成称为同步,而以通过回调通知的方式获得完成标志则称为异步。
阻塞与非阻塞:在那段时间差的过程中,CPU有没有处理别的事情,如果处理过别的事情则是非阻塞,如果并没有处理过别的事情则是阻塞。
*1,2代表轮询I/O操作的进行状态,3代表I/O操作已经完成
我们把一个I/O调用看做上面A和B两个过程,A阶段是CPU发出I/O调用【此阶段是十分快速的】,B阶段是相关设备把数据从目标位置转移到用户空间的过程【此阶段就会由于数据量以及数据所在设备的远近而所用时间大为不同】,容易明白的是上面四个概念都是针对B阶段在数据迁移过程中此进程/线程所对应的CPU状态而言的,所以用这个过程来看看上面四个概念的组合:
1.同步阻塞:即是在B阶段CPU一直采用轮询的方式直到获得完成标志,所以此段时间CPU一直阻塞在此I/O操作上。
2.同步不阻塞:在B阶段依然采用轮询的方式直至获得完成标志,但是此轮询不同于上面的轮询过程,而是在相邻的轮询中完成了上下文切换去处理别的任务的,所以是同步不阻塞
3.异步阻塞:也就是所没有上面的1,2过程,当I/O操作完成后回调通知CPU已完成【即是3过程】,但是此阶段CPU处于休眠状态而不处理别的任务。
4.异步不阻塞:和上面一样没有1,2过程而是通过回调知道I/O操作已完成,但是并没有休眠,而是在此阶段处理其他任务。
综上所述:异步不阻塞是最高效的。
在实际中常采用多线程模拟理想异步非阻塞模式:一个主线程用于计算,多个线程用于执行I/O操作【可能是上面四种的任意形式】
几种常见服务器模型:
1.同步式:一次处理一个请求,其余请求处于等待状态
2.每请求/每进程: 为每个请求启动一个进程【不具备扩展功能,系统志愿有限】
3.每请求/每线程:为每个请求启动一个线程【每个线程占一定内存,故受限于内存,还会拖慢服务器】【Apache就是采用的这种方式】
4.事件驱动:Node与Nginx采用事件驱动方式而不创建新线程【省去了线程创建/删除的系统开销以及线程上下文切换,所以能够处理更多的连接】【python的Twisted,Ruby的Event Machine以及Perl的AyEvent也是事件驱动,但是他们并不是很成功】
需要注意的是对于高并发【注释1】的程序往往采用“同步非阻塞”而不是“多线程的同步阻塞”,在合理设计任务调度的不同阶段可使得并发数远大于并行数,需要注意的是在高并发状况下为每个任务创建一个线程的开销很大,所以并不采用多线程的同步阻塞。
注释1:并发:同时进行的任务数量
并行:可同时工作的物理资源(CPU核数等等)
参考:
1:http://blog.jobbole.com/99765/
2:深入浅出Node.js