同步、异步、阻塞、非阻塞与服务器


最近在折腾服务器框架相关问题,学了东西不记下来很快会忘记……

OS中的同步、异步、阻塞、非阻塞

OS的I/O流程是这样的:

  1. CPU发出I/O操作的通知;
  2. 文件系统或其他会调用相关设备执行这些操作;
  3. 最后当数据到达用户空间后发出一个中断的完成标志;

在这个从CPU发出调用到收到完成标志的过程中就存在一个时间差,现在就有了两个重要的概念:完成标志与时间差。

  • 同步与异步是针对获取完成标志
  • 阻塞与非阻塞是针对时间差

所以有这样的定义:

  • 同步与异步:获取完成标志的方式。如果是采用轮询的方式监测I/O操作是否完成称为同步,而以通过回调通知的方式获得完成标志则称为异步。
  • 阻塞与非阻塞:在那段时间差的过程中,CPU有没有处理别的事情,如果处理过别的事情则是非阻塞,如果并没有处理过别的事情则是阻塞。

I/O控制方式有哪几种?
1)阻塞I/O(blocking I/O)
2)非阻塞I/O (nonblocking I/O)--> 轮询

  1. I/O复用(select 和poll) (I/O multiplexing)
    4)信号驱动I/O (signal driven I/O (SIGIO))
    5)异步I/O (asynchronous I/O (the POSIX aio_functions)) --> 回调
    看图便于理解:

    出自:Linux的五种I/O模式

几种常见服务器模型:

下面我们谈谈服务器,服务器模型也可以基于异步、同步、阻塞、非阻塞这四个概念

  • 同步式:一次处理一个请求,其余请求处于等待状态
  • 每请求/每进程: 为每个请求启动一个进程【不具备扩展功能,系统志愿有限】
  • 每请求/每线程:为每个请求启动一个线程【每个线程占一定内存,故受限于内存,还会拖慢服务器】【Apache就是采用的这种方式】
  • 事件驱动:Node与Nginx采用事件驱动方式而不创建新线程【省去了线程创建/删除的系统开销以及线程上下文切换,所以能够处理更多的连接】【python的Twisted,Ruby的Event Machine以及Perl的AyEvent也是事件驱动,但是他们并不是很成功】

需要注意的是对于高并发的程序往往采用“同步非阻塞”而不是“多线程的同步阻塞”,在合理设计任务调度的不同阶段可使得并发数远大于并行数,需要注意的是在高并发状况下为每个任务创建一个线程的开销很大,所以并不采用多线程的同步阻塞。

并发:同时进行的任务数量
并行:可同时工作的物理资源(CPU核数等等)

另外,有个概念是异步IO,其主要是说在同一线程中当遭遇IO时,并不等待而是执行下面的操作直到IO操作完成后再切回当前,其实就是同步非阻塞,在廖雪峰老师的博客中提到这样一个模型:
异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程:

loop = get_event_loop()
while True:
    event = loop.get_event()
    process_event(event)

消息模型其实早在应用在桌面应用程序中了。一个GUI程序的主线程就负责不停地读取消息并处理消息。所有的键盘、鼠标等消息都被发送到GUI程序的消息队列中,然后由GUI程序的主线程处理。

由于GUI线程处理键盘、鼠标等消息的速度非常快,所以用户感觉不到延迟。某些时候,GUI线程在一个消息处理的过程中遇到问题导致一次消息处理时间过长,此时,用户会感觉到整个GUI程序停止响应了,敲键盘、点鼠标都没有反应。这种情况说明在消息模型中,处理一个消息必须非常迅速,否则,主线程将无法及时处理消息队列中的其他消息,导致程序看上去停止响应。

消息模型是如何解决同步IO必须等待IO操作这一问题的呢?当遇到IO操作时,代码只负责发出IO请求,不等待IO结果,然后直接结束本轮消息处理,进入下一轮消息处理过程。当IO操作完成后,将收到一条“IO完成”的消息,处理该消息时就可以直接获取IO操作结果。

在“发出IO请求”到收到“IO完成”的这段时间里,同步IO模型下,主线程只能挂起,但异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息。这样,在异步IO模型下,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。对于大多数IO密集型的应用程序,使用异步IO将大大提升系统的多任务处理能力。

相关资料

Linux的五种I/O模式
廖雪峰Python-异步I/O
同步,异步,阻塞,非阻塞以及几种常见的服务器模型

posted @ 2018-05-17 22:11  blackay03  阅读(868)  评论(0编辑  收藏  举报
Live2D
*/