系统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. Java随机生成定长纯数字或数字字母混合数

    (转)Java随机生成定长纯数字或数字字母混合数 运行效果图: 具体实现代码

  2. 查看alter错误,grep -A,-B,-C的妙用

    alert 日志记录了数据库的很多重要信息,要养成时常检查alert日志的习惯,但如果日志很大vi打开翻来覆去找着麻烦,怎么做的可以查错呢? 看我的测试 [oracle@ahjcyl-db bdump ...

  3. Oracle学习【索引及触发器】

    索引B_Tree结构 请参照 响应图例 索引是一种允许直接访问数据表中某一数据行的树形结构,为了提高查询效率而引入,是独立于表的对象,可以存放在与表不同的表空间中.索引记录中存有索引关键字和指向表中数 ...

  4. wamp使用方法【总】

    Apache与php配置:我们把php-5.2.9-Win32.zip解压到C盘根目录下,把文件夹名字改成PHP,这样方便一下. 1. 找到PHP目录下的“php.ini-dist”或者“php.in ...

  5. CSS 创建

    当读到一个样式表时,浏览器会根据它来格式化 HTML 文档. 如何插入样式表 插入样式表的方法有三种: 外部样式表 内部样式表 内联样式 外部样式表 当样式需要应用于很多页面时,外部样式表将是理想的选 ...

  6. PHP程序开发人员要掌握的知识

    文件目录处理函数包80%以上的函数的功能的灵活运用. 日期时间函数中的80%以上的函数的功能的灵活运用 数学函数库中的100%的内容. 网络库中的60%以上的内容,对各个函数的功能比较熟悉. 字符串处 ...

  7. angularjs应用骨架

    使用典型的类库时,你可以选择并使用你所喜欢的功能:而对于angularjs框架来说,必须把它看成一个完整的套件来使用,框架中的所有的东西都包含在里面,接下来将会介绍angular的基础模块,这样你就可 ...

  8. Windows phone 之样式的关联

    建议大家做界面要用Blend. 做过web的都知道DIV+CSS,给页面元素关联样式有三种方式: 1.内联样式表:就是在每个元素里面写style.优点就是灵活,给指定的元素添加样式.缺点是重用性很差, ...

  9. Linux权限体系总结

    总结 1.  LINUX9位权限及rwx字符的作用.对应的数字及对应的用户和用户组图解. 2.  rwx对文件来说代表什么意思. 3.  rwx对目录来说代表什么意思. 4.  企业生产环境目录设置权 ...

  10. Ajax and JSON

    Ajax (核心是XMLHttpRequest对象) 1.XMLHttpRequest对象: request=new XMLHttpRequest()  支持Firefox opera Safari  ...