reactor & proactor

文章目录
  1. 1. 标准定义
    1. 1.1. 在 Reactor 中实现读:
    2. 1.2. 在 Proactor 中实现读:
  2. 2. 通俗理解
  • 参考资料
  • 标准定义

    两种I/O多路复用模式:Reactor 和 Proactor

    一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的 read/write 事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。

    两个与事件分离器有关的模式是Reactor和Proactor。Reactor 模式采用同步 IO,而 Proactor 采用异步 IO。

    在 Reactor 中,事件分离器负责等待文件描述符或 socket 为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。

    而在 Proactor 模式中,处理器–或者兼任处理器的事件分离器,只负责发起异步读写操作。IO 操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址和数据大小,操作系统才能从中得到写出操作所需数据,或写入从 socket 读到的数据。事件分离器捕获 IO 操作完成事件,然后将事件传递给对应处理器。比如,在 windows 上,处理器发起一个异步 IO 操作,再由事件分离器等待 IOCompletion 事件。典型的异步模式实现,都建立在操作系统支持异步 API 的基础之上,我们将这种实现称为“系统级”异步或“真”异步,因为应用程序完全依赖操作系统执行真正的IO工作。

    举个例子,将有助于理解 Reactor 与 Proactor 二者的差异,以读操作为例(类操作类似)。

    在 Reactor 中实现读:

    1. 注册读就绪事件和相应的事件处理器
    2. 事件分离器等待事件
    3. 事件到来,激活分离器,分离器调用事件对应的处理器。
    4. 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

    在 Proactor 中实现读:

    1. 处理器发起异步读操作(注意:操作系统必须支持异步 IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
    2. 事件分离器等待操作完成事件
    3. 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。
    4. 事件分离器呼唤处理器。
    5. 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

    可以看出,两个模式的相同点,都是对某个 IO 事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上,两者也有相同点:demultiplexor 负责提交 IO 操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;不同点在于,异步情况下(Proactor),当回调 handler 时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write)。

    通俗理解

    使用Proactor框架和Reactor框架都可以极大的简化网络应用的开发,但它们的重点却不同。

    Reactor 框架中用户定义的操作是在实际操作之前调用的。比如你定义了操作是要向一个 SOCKET 写数据,那么当该S OCKET 可以接收数据的时候,你的操作就会被调用;而 Proactor 框架中用户定义的操作是在实际操作之后调用的。比如你定义了一个操作要显示从 SOCKET 中读入的数据,那么当读操作完成以后,你的操作才会被调用。

    Proactor 和 Reactor 都是并发编程中的设计模式。在我看来,他们都是用于派发/分离 IO 操作事件的。这里所谓的 IO 事件也就是诸如 read/write 的 IO 操作。”派发/分离”就是将单独的 IO 事件通知到上层模块。两个模式不同的地方在于,Proactor 用于异步 IO,而 Reactor 用于同步 IO。

    参考资料