Reactor是什么?跟Proactor的区别?
Reactor
Reactor模式是处理并发I/O比较常见的一种模式,中心思想就是,将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。
Reactor模型有三个重要的组件:
多路复用器:由操作系统提供,在linux上一般是select, poll, epoll等系统调用。
事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。
事件处理器:负责处理特定事件的处理函数。
具体流程如下:
- 注册读就绪事件和相应的事件处理器。
- 事件分离器等待事件。
- 事件到来,激活分离器,分离器调用事件对应的处理器。
- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
优点:
- 响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;
- 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
- 可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;
- 可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;
Proactor
Proactor最大的特点是使用异步I/O。所有的I/O操作都交由系统提供的异步I/O接口去执行。工作线程仅仅负责业务逻辑。
在Proactor中,用户函数启动一个异步的文件操作。同时将这个操作注册到多路复用器上。
多路复用器并不关心文件是否可读或可写而是关心这个异步读操作是否完成。异步操作是操作系统完成,用户程序不需要关心。
多路复用器等待直到有完成通知到来。当操作系统完成了读文件操作——将读到的数据复制到了用户先前提供的缓冲区之后,通知多路复用器相关操作已完成。多路复用器再调用相应的处理程序,处理数据。
具体流程如下:
- 处理器发起异步操作,并关注I/O完成事件
- 事件分离器等待操作完成事件
- 分离器等待过程中,内核并行执行实际的I/O操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成
- I/O完成后,通过事件分离器呼唤处理器
- 事件处理器处理用户自定义缓冲区中的数据
异同
两个模式的相同点,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调注册处理函数。
不同点在于,异步情况下(Proactor),当回调注册的处理函数时,表示IO操作已经完成;同步情况下(Reactor),回调注册的处理函数时,表示IO设备可以进行某个操作(can read or can write),注册的处理函数这个时候开始提交操作。
解释同步和异步
同步和异步是针对应用程序和内核的交互而言的
同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪
异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知
解释阻塞和非阻塞
阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式
阻塞方式下读取或者写入函数将一直等待
非阻塞方式下,读取或者写入函数会立即返回一个状态值