系统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. mvc5 + ef6 + autofac搭建项目(repository+uow)(二)

    续上篇: DBContext 在上篇 图一类库根目录创建的 DbContextBase /// <summary> /// 数据库上下文基类 /// </summary> // ...

  2. Perl连接Sqlite数据库

    Sqlite是一个小巧的嵌入式关系型数据库,几乎可以嵌入所有编程语言,特别是C,C++,PHP,Perl等.这里就介绍如何用Perl连接并操作Sqlite数据库. use DBI; # perl用以操 ...

  3. 【转】怎样创建一个Xcode插件(Part 2)

    原文:How To Create an Xcode Plugin: Part 2/3 原作者:Derek Selander 译者:@yohunl 译者注:原文使用的是xcode6.3.2,我翻译的时候 ...

  4. (转)Android开发:性能最佳实践-管理应用内存

    翻自:http://developer.android.com/training/articles/memory.html 在任何软件开发环境中,RAM都是宝贵的资源,但在移动操作系统中更加珍贵.尽管 ...

  5. U盘安装ubuntu时出现的gfxboot.c32:not a COM32R image问题

    方法特别简单:只需在提示后面输入   live  然后回车 就OK了

  6. bootstrap 下 标签页跳转总结

    最近遇到一个问题,是关于bootstrap中的标签页实现上的一些功能实现,现总结一下. 问题描述:点击其他标签页后,如何在点击搜索按钮后自动跳转到第一个标签页.如下图 通过对bootstrap框架里的 ...

  7. ERP系统开发平台 (C#语言,支持多数据库)

    C/S系统开发框架-企业版 V4.0 (Enterprise Edition) 简介: http://www.csframework.com/cs-framework-4.0.htm 适用软件:适合开 ...

  8. 精通 Oracle+Python,第 7 部分:面向服务的 Python 架构

    面向服务的架构 (SOA) 在当今的业务战略中具有至关重要的作用.混搭企业组件已成为所有任务关键的企业应用程序的标准要求,从而确保在企业架构的各层实现顺畅的服务编排.对此,Python 是一个不错的选 ...

  9. Junit4 架构设计系列(2): Runner.run()与Statement

    Overall 系列入口: Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder 前文中,我们基本理清了Junit4执行Case大体上的Flow ...

  10. Linq 数据合并,比较,连接,交叉 维恩图工具

    Except 返回包含两个不同之处的linq结果集 Intersect 返回两个容器中共同的数据项 Union 返回所有成员,相同的成员出现多次,将只返回一个 Concat 返回所有数据项