关于Reactor和Proactor的差别
/********************************************************************* * Author : Samson * Date : 07/12/2015 * Test platform: * gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 * GNU bash, 4.3.11(1)-release (x86_64-pc-linux-gnu) * Nginx version: * Nginx 1.6.2 * Nginx 1.8.0 * *******************************************************************/
系统I/O 可分为堵塞型。 非堵塞同步型以及非堵塞异步型。
堵塞型I/O意味着控制权仅仅到调用操作结束了才会回到调用者手里。
非堵塞同步是会马上返回控制权给调用者的。
调用者不须要等等。它从调用的函数获取两种结果:要么此次调用成功进行了;要么系统返回错误标识告诉调用者当前资源不可用,你再等等或者再试度看吧。比方read()操作。 假设当前socket无数据可读,则马上返回EWOULBLOCK/EAGAIN,告诉调用read()者”数据还没准备好,你稍后再试”。
非堵塞异步调用中,稍有不同。调用函数在马上返回时,还告诉调用者。这次请求已经開始了。系统会使用另外的资源或者线程来完毕这次调用操作。并在完毕的时候知会调用者(比方通过回调函数)。POSIX的aio_read()来说。调用它之后,函数马上返回,操作系统在后台同一时候開始读操作。即是将工作交给了内核去完毕这个操作。
在以上三种IO形式中。非堵塞异步是性能最高、伸缩性最好的。
两种IO多路复用方案:Reactor and Proactor
普通情况下,I/O 复用机制须要事件分享器(event demultiplexor)。 事件分享器的作用,即将那些读写事件源分发给各读写事件的处理者。就像送快递的在楼下喊: 谁的什么东西送了, 快来拿吧。
开发者在開始的时候须要在分享器那里注冊感兴趣的事件。并提供对应的处理者(event handlers),或者是回调函数; 事件分享器在适当的时候会将请求的事件分发给这些handler或者回调函数。
涉及到事件分享器的两种模式称为:Reactor and Proactor。 Reactor模式是基于同步I/O的,而Proactor模式是和异步I/O相关的。
在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比方文件描写叙述符可读写。或者是socket可读写),事件分离者就把这个事件传给事先注冊的事件处理函数或者回调函数,由后者来做实际的读写操作。
而在Proactor模式中。事件处理者(或者代由事件分离者发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完毕的。发起时。须要提供的參数包含用于存放读到数据的缓存区,读的数据大小,或者用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分离者得知了这个请求,它默默等待这个请求的完毕,然后转发完毕事件给对应的事件处理者或者回调。
这样的异步模式的典型实现是基于操作系统底层异步API的。所以我们可称之为“系统级别”的或者“真正意义上”的异步。由于详细的读写是由操作系统代劳的。
举另外个样例来更好地理解Reactor与Proactor两种模式的差别。这里我们仅仅关注read操作。由于write操作也是差点儿相同的。
以下是Reactor的做法:
某个事件处理者宣称它对某个socket上的读事件非常感兴趣;
事件分离者等着这个事件的发生;
当事件发生了,事件分离器被唤醒,这负责通知先前那个事件处理者;
事件处理者收到消息,于是去那个socket上读数据了。 假设须要,它再次宣称对这个socket上的读事件感兴趣,一直反复上面的步骤;
以下再来看看真正意义的异步模式Proactor是怎样做的:
事件处理者直接投递发一个写操作(当然,操作系统必须支持这个异步操作)。 这个时候。事件处理者根本不关心读事件,它仅仅管发这么个请求,它魂牵梦萦的是这个写操作的完毕事件。
这个处理者非常拽,发个命令就无论详细的事情了。仅仅等着别人(系统)帮他搞定的时候给他回个话。
事件分离者等着这个读事件的完毕(比較下与Reactor的不同);
当事件分离者默默等待完毕事情到来的同一时候,操作系统已经在一边開始干活了,它从目标读取数据,放入用户提供的缓存区中。最后通知事件分离者,这个事情我搞完了;
事件分享者通知之前的事件处理者: 你吩咐的事情搞定了;
事件处理者这时会发现想要读的数据已经乖乖地放在他提供的缓存区中,想怎么处理都行了。假设有须要。事件处理者还像之前一样发起另外一个写操作,和上面的几个步骤一样。
标准的经典的 Reactor模式:
步骤 1) 等待事件 (Reactor 的工作)
步骤 2) 发”已经可读”事件发给事先注冊的事件处理者或者回调 ( Reactor 要做的)
步骤 3) 读数据 (用户代码要做的)
步骤 4) 处理数据 (用户代码要做的)
模拟的Proactor模式:
步骤 1) 等待事件 (Proactor 的工作)
步骤 2) 读数据(看。这里变成成了让 Proactor 做这个事情)
步骤 3) 把数据已经准备好的消息给用户处理函数,即事件处理者(Proactor 要做的)
步骤 4) 处理数据 (用户代码要做的)
在没有底层异步I/O API支持的操作系统。这样的方法能够帮我们隐藏掉socket接口的差异(不管是性能还是其他)。 提供一个全然可用的统一“异步接口”。这样我们就能够开发真正平台独立的通用接口了。
那么,综上所述,这两者的差别是什么呢?
简单直观的理解:
1、Reactor模式是等待关心的动作的发生后。将怎样处理这个动作的兴许交给了用户态的应用本身来处理。Reactor的事件分享器仅仅关心事件的发生。其他的就全然交给应用程序来处理了;而Proactor模式则是仅仅关心由操作系统(内核)完毕异步非堵塞的操作后返回的结果。
2、Proactor场景中仅仅可以使用异步非堵塞的syscall(系统调用)。而Reactor的场景中很多其它地是使用非堵塞同步的syscall(系统调用);
对于不明确
IO - 同步,异步,堵塞,非堵塞概念的请先看下这个科普:
http://blog.csdn.net/historyasamirror/article/details/5778378
REF:
http://www.artima.com/articles/io_design_patterns.html