系统I/O 可分为阻塞型, 非阻塞同步型,非阻塞异步型。 (Linux对aio支持的不完整,所以linux上用Reactor比较多;Proactor需要系统API支持真正的“异步”)

阻塞型I/O意味着控制权直到调用操作结束才会返回到调用者手里。因此调用者被阻塞了, 这段时间了做不了任何其它事情. 更郁闷的是,在等待IO结果的时间里,调用者所在线程此时无法腾出手来去响应其它的请求。拿read()操作来说吧, 调用此函数的代码会一直僵在此处直至它所读的socket缓存中有数据到来.

相比之下,非阻塞同步是会立即返回控制权给调用者的。调用者不需要等等,它从调用的函数获取两种结果:要么此次调用成功进行了;要么系统返回错误标识告诉调用者当前资源不可用,你再等等或者再试度看吧。比如一个read()操作对sockect在非阻塞模式下,返回成功读取的字节数,或者失败返回特殊码-1设置为EWOULBLOCK/EAGAIN,告诉调用read()者"数据还没准备好,你稍后再试".

在非阻塞异步调用中,稍有不同。调用函数在立即返回时,还告诉调用者,这次请求已经开始了。系统会使用另外的资源或者线程来完成这次调用操作,并在完成的时候知会调用者(比如通过回调函数)。拿Windows的ReadFile()或者POSIX的aio_read()来说,调用它之后,函数立即返回,操作系统在后台同时开始读操作。 操作系统提供了异步的模式来传输网络数据,即:应用程序把要发送的数据交给操作系统,操作系统把数据放在系统缓冲区后应用程序该干嘛干嘛去。操作系统发送完成后,会给应用系统一个回执,告诉应用程序:刚才那个包发送完成了!

在以上三种IO形式中,非阻塞异步是性能最高、伸缩性最好的。  Proactor模式在单CPU单核系统应用中有着无可比拟的优势。

二者的差异,以读操作为例(写操作类似)。
 在Reactor中实现读
 - 注册读就绪事件和相应的事件处理器
 - 事件分离器等待事件
 - 事件到来,激活分离器,分离器调用事件对应的处理器。
 - 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
 与Proactor(真异步)中的读过程比较:
 - 事件处理者直接发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
 - 事件分离器等待操作完成事件 (比较下与Reactor的不同)
 - 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,通知事件分离器读操作完成。
 - 事件分离器呼唤之前的事件处理者事情搞定了。
 - 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

改进的解决方案

将Reactor稍做调整,模拟成异步的Proactor模型(主要是在事件分离器里完成本该事件处理者做的实际读写工作,我们称这种方法为"模拟异步")。 下面的示例可以看看read操作是如何完成的:

  • 事件处理者宣称对读事件感兴趣,并提供了用于存储结果的缓存区、读数据长度等参数;
  • 分离器等待(比如通过select());
  • 当有事件到来(即可读),分离器被唤醒, 分离器去执行非阻塞的读操作(前面事件处理者已经给了足够的信息了)。读完后,它去通知事件处理者。
  • 事件处理器处理用户自定义缓冲区的数据,注册新的事件(当然同样要给出数据缓冲区地址,需要读取的数据量等信息),最后将控制权返还分离器。

我们看到,通过为分离者添加一些功能,可以让Reactor模式转换为Proactor模式。所有这些被执行的操作,其实是和Reactor模型应用时完全一致的。我们只是把工作打散分配给不同的角色去完成而已。这样并不会有额外的开销,也不会有性能上的的损失,我们可以再仔细看看下面的两个过程,他们实际上完成了一样的事情:

标准的经典的 Reactor模式:

  • 步骤 1) 等待事件 (Reactor 的工作)
  • 步骤 2) 发"已经可读"事件发给事先注册的事件处理者或者回调 ( Reactor 要做的)
  • 步骤 3) 读数据 (用户代码要做的)
  • 步骤 4) 处理数据 (用户代码要做的)

模拟的Proactor模式:

  • 步骤 1) 等待事件 (Proactor 的工作)
  • 步骤 2) 读数据(这里变成了让 Proactor 做这个事情)
  • 步骤 3) 把数据已经准备好的消息给用户处理函数,即事件处理者(Proactor 要做的)
  • 步骤 4) 处理数据 (用户代码要做的)

在没有底层异步I/O API支持的操作系统,这种方法可以帮我们隐藏掉socket接口的差异(无论是性能还是其它), 提供一个完全可用的统一"异步接口"。这样我们就可以开发真正平台独立的通用接口了。

Boost.Asio类库,其就是以Proactor这种设计模式来实现,参见:Proactor(The Boost.Asio library is based on the Proactor pattern. This design note outlines the advantages and disadvantages of this approach.),其设计文档链接:http://asio.sourceforge.net/boost_asio_0_3_7/libs/asio/doc/design/index.html

所有同步等待策略可划分为两组:

  • edge-triggered (e.g. Linux实时信号) - signal readiness only when socket became ready (changes state);
  • level-triggered (e.g. select()poll(), /dev/poll) - readiness at any time.

如:ACE技术论文集-第8章 前摄器(Proactor):用于为异步事件多路分离和分派处理器的对象行为模式

ACE技术论文集-第7章 ACE反应堆(Reactor)的设计和使用:用于事件多路分离的面向对象构架

ACE程序员教程-第6章 反应堆(Reactor):用于事件多路分离和分派的体系结构模式

ACE应用-第2章 JAWS:高性能Web服务器构架

高性能I/O设计模式Reactor和Proactor的更多相关文章

  1. 两种高性能 I/O 设计模式 Reactor 和 Proactor

    两种高性能 I/O 设计模式 Reactor 和 Proactor Reactor 和 Proactor 是基于事件驱动,在网络编程中经常用到两种设计模式. 曾经在一个项目中用到了网络库 libeve ...

  2. I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  3. 【转载】高性能I/O设计模式Reactor和Proactor

    转载自:http://blog.csdn.net/roger_77/article/details/1555170 昨天购买了<程序员>杂志 2007.4期,第一时间去翻阅了一遍,其中有一 ...

  4. I/O模型系列之四:两种高性能IO设计模式 Reactor 和 Proactor

    不同的操作系统实现的io策略可能不一样,即使是同一个操作系统也可能存在多重io策略,常见如linux上的select,poll,epoll,面对这么多不同类型的io接口,这里需要一层抽象api来完成, ...

  5. 两种高性能I/O设计模式(Reactor/Proactor)的比较

    原文出处: Alex Libman   译文出处:潘孙友   欢迎分享原创到伯乐头条 综述 这篇文章探讨并比较两种用于TCP服务器的高性能设计模式. 除了介绍现有的解决方案,还提出了一种更具伸缩性,只 ...

  6. [转]两种高性能I/O设计模式(Reactor/Proactor)的比较

    [原文地址:http://www.cppblog.com/pansunyou/archive/2011/01/26/io_design_patterns.html] 综述 这篇文章探讨并比较两种用于T ...

  7. 高性能IO设计的Reactor和Proactor模式(转)

    在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...

  8. I/O模型之四:Java 浅析I/O模型(BIO、NIO、AIO、Reactor、Proactor)

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  9. IO设计模式:Reactor和Proactor对比

    IO设计模式:Reactor和Proactor对比 平时接触的开源产品如Redis.ACE,事件模型都使用的Reactor模式:而同样做事件处理的Proactor,由于操作系统的原因,相关的开源产品也 ...

随机推荐

  1. SET QUOTED_IDENTIFIER OFF语句的作用

    先看下面几个sql语句  1 SET QUOTED_IDENTIFIER ON 2 SELECT * FROM "USER"    WHERE a='netasp'  3  4 S ...

  2. Javascript字符串拼接小技巧

    在Javascript中经常会遇到字符串的问题,但是如果要拼接的字符串过长就比较麻烦了. 如果是在一行的,可读性差不说,如果要换行的,会直接报错. 在此介绍几种Javascript拼接字符串的技巧. ...

  3. Java OOP考试错题分析

      解析: A.ArrayList 可以存储NULL值,也可以存储重复的值,对集合没有任何影响. B.一旦实例化不可改变自身大小,这是数组的特性.集合的容量是自身扩容的. C.ArrayList可以存 ...

  4. 获取数据后导出Excel

    List<PortResourceInfo> list = getList()//获取数据源 //导出excle Response.Clear(); Response.ContentTyp ...

  5. 问题:Bringing up interface eth0: Device eth0 does not seem to be present,delaying initialization. [FAILED]—— 找不到网卡。

    克隆虚拟机的时候或其他情况出现以下问题(命令service network restart):   Bringing up interface eth0:  Device eth0 does not ...

  6. sencha touch中按钮的ui配置选项值及使用效果

  7. PHP表单常用正则表达式(URL、HTTP、手机、邮箱等)

    <?php /** * @description: 正则表达式匹配 */ class Regex { /** * @手机号 */ public static function Phone($su ...

  8. #Leet Code# Unique Tree

    语言:Python 描述:使用递归实现 class Solution: # @return an integer def numTrees(self, n): : elif n == : else: ...

  9. [python]随机数

    import random()testlist = [1,3,4,5]a,b = 1,5random().random()() 生成0至1之间的随机浮点数,结果大于等于0.0,小于1.0random. ...

  10. 在oj平台上练习的一些总结【转】

    程序书写过程中的一些小技巧:1. freopen(“1.txt”,”r”,stdin); //程序运行后系统自动输入此文档里面的内容(不需要进行手动输入)freopen(“1.txt”,”w”,std ...