关于Blocking IO,non-Blokcing IO,async IO的区别和理解

来源:http://shmilyaw-hotmail-com.iteye.com/blog/1896683

概括来说,一个IO操作可以分为两个部分:发出请求、结果完成。如果从发出请求到结果返回,一直Block,那就是Blocking IO(常见的顺序化程序结构);如果发出请求就可以返回(结果完成不考虑),就是non-blocking IO;如果发出请求就返回,结果返回是Block在select或者poll上的,则其只能称为IO multiplexing;如果发出请求就返回,结果返回通过Call Back的方式被处理,就是AIO。

文[2]中图画的不错,说的也比较清楚借来用一下。

 

Blocking IO

    先从这个开始讨论起,在前面的文章里,我们知道一个server端的程序要和一个客户端通信,它首先自己要绑定到一个端口,然后调用accept方法来接收一个客户端的请求并建立连接。这个accept返回的socket连接可以获取到双方通信的InputStream和OutputStream,这样双方就可以通过这两个Stream来互相发消息了。

    当然,对于单独的一个server进程和一个client进程来说,这是一个典型的场景。但是如果对于有多个client的情况下,我们该怎么来处理呢?一种典型的思路就是,我们针对每一个client连接请求建立一个thread来处理。这种模式如下图:

 按照这种思路,针对每一个client,我们都有一个对应的handler来处理他们的业务逻辑。每个handler对应一个线程。

  我们再想想,在有大量用户连接的情况下,server将针对所有连接client创建thread。如果有成千上万的连接的话,这将是一个很大的开销。由于系统资源实际的限制,可能会占用大量的资源甚至会导致资源被消耗光的情况。另外,这么多个线程连接需要处理,CPU需要在多个线程之间切换。在大量线程的情况下,切换的开销也很大。在这些情况下,请求量大的时候系统的性能和资源利用率都不高。那么我们有没有什么办法可以提高资源利用率和性能呢?


Non-Blocking IO

    除了前面提到的blocking IO,其实还有一种io的方式,就是Non-Blocking IO。在详细讨论Non-Blocking IO之前,我们先看看原来blocking IO的一些不足。我们前面的每个连接一个线程的方式如下图所示:

 

 

     对于每一个连接的所有操作,从建立连接,准备数据,读取数据,编码,解码以及来回发送数据,都在一个线程里全包了。之所以会出现前面提到的资源利用率不高和系统性能受到影响,就是因为通过socket进行io的系统性能和系统本身进程性能本身是有大的差异的。一般来说,IO的性能比系统CPU的性能要差几个数量级。所以才会有大量线程要处理数据的时候,都卡在这里等IO了。这就是前面这种方式有问题的根源所在了。

  那么,我们有没有什么办法可以改进一下呢?我们这个时候可以考虑一下异步程序执行的思路。一般对于异步程序执行来说,在调用某一个方法的时候,这个被调用的方法在另外的一个进程或者线程中执行。这个调用方法的进程并不等被调用的方法返回结果,而是继续执行自己后面的过程。可是,在调用的目标方法结束之后,我们怎么让这个调用方法的进程知道方法执行结束了并知道方法的结果呢?这个时候,我们很多时候会使用一种回调的机制。关于回调机制的思路,和我前面一篇讨论Observer模式的文章说的非常近似。笼统的来说,就是我这个调用方法的进程实现注册好了一个通知的机制,然后在被调用方法结束后,这个被调用的方法进程通过这个机制来通知我。

    如果我们借鉴这个思路,比如说前面有一些客户端连接服务器端,它们只需要把要操作的目的资源,比如写某个文件之类的,针对哪个socket等等关联起来。等一旦socket连接准备好了再触发它们。这样就不需要开这么多个线程来等了。这些socket连接ready等行为相当于一个定义的一个事件被触发了。我们很多要被回调的方法都放在一个事件通知的队列里。这些回调事件的执行可以放到单独的一个线程里执行。这样也就不需要前面那么多的线程,也减少了线程间切换的开销。




Blocking IO

这个最好理解了,在Blocking IO模式下,函数调用只有在操作完成后才会返回。下图是它调用过程的图示:

 

重点解释下上图,下面例子都会讲到。首先application调用 recvfrom()转入kernel,注意kernel有2个过程,wait for data和copy data from kernel to user。直到最后copy complete后,recvfrom()才返回。此过程一直是阻塞的。

 

Non-Blocking IO

Non-Blocking 是Blocking的反,也就是说,即使操作没有完成,函数也可以返回。调用过程如下:

 

可以看见,如果直接操作它,那就是个轮询。。直到内核缓冲区有数据


AIO也是这样啊?对!这是Non-Blocking IO 和AIO的共同点。其实从概念层面来说Non-Blocking IO 就是AIO,他们没有什么区别。但是Non-Blocking IO是对文件描述符(*nix)或者Handle(Windows)的设置,在执行操作时不需要特殊的数据结构。Non-Blocking IO提交请求后只能通过提交的操作函数来查询操作是否完成,这是一个很大的限制。而AIO往往会提供多种通知或者查询机制,也就是说用Non-Blocking IO时只能轮询,而AIO有更多选择(其它通知机制)。所以是否支持轮询外的其他机制是AIO和Non-Blocking IO的区别。

I/O multiplexing (select and poll)
最常见的I/O复用模型,select。

 

select先阻塞,有活动套接字才返回。与blocking I/O相比,select会有两次系统调用,但是select能处理多个套接字。




posted @ 2018-03-08 17:41  星朝  阅读(600)  评论(0编辑  收藏  举报