一、Reactor模式(反应器模式)
1、Reactor模式的特点
Reactor用于同步I/O,同步是指用户进程触发IO操作并等待或去轮询的查看I/O操作是否就绪,如果事件就绪的话需要应用程序自己读取或写入数据。(Reactor模式需要用户自己进行I/O操作)。
并发系统常用Reactor模式代替常用的多线程的处理方式,节省系统的资源,提高系统吞吐量。
2、使用同步I/O模型(以epoll_wait为例)实现的Reactor模式的工作流程:
2.1、主线程往epoll内核事件表里面注册socket上的读事件。
2.2、主线程调用epoll_wait等待socket上有数据可读。
2.3、当socket上有数据可读时,epoll_wait通知主线程。主线程将socket可读事件放入请求队列。
2.4、睡眠在请求队列上的某个工作线程被唤醒,它从socket上读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写事件。
2.5、主线程调用epoll_wait等待socket可写。
2.6、当socket可写时,epoll_wait通知主线程。主线程将socket上的可写事件放入请求队列。
2.7、睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
3、使用实例
poll实现聊天系统
二、Proactor模式(主动器模式)
1、Proactor模式的特点
Proactor用于异步I/O,异步是指用户进程触发I/O操作以后便做自己的事情,而当I/O操作完成的时候会通知程序。Proactor模式中,应用程序不需要进行实际的读写过程,全部交给内核去做,内核做完之后再通知应用程序。
2、使用异步I/O模型(aio_read和aio_write为例)实现的Proactor模式的工作流程:
2.1、主线程调用aio_read函数向内核注册socket上的读事件,并告诉内核用户缓冲区的位置,和缓冲区大小,以及读操作完成时如何通知应用程序。(以信号为例)
2.2、主线程继续处理其他逻辑。
2.3、当socket上的数据被读入用户缓冲区之后,内核将向应用程序发送一个信号,告诉应用程序数据已经可用。
2.4、应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完成之后,调用aio_write函数向内核注册socket上的写事件,并告诉内核用户写缓冲区的位置和大小,以及写操作完成时如何通知应用程序。
2.5、主线程继续处理其他逻辑。
2.6、当用户缓冲区的数据被写入到socket之后,内核将向应用程序发送一个信号,通知应用程序写事件已经完毕。
2.7、应用层序预先定义好的信号处理函数选择一个工作线程来做善后处理。
三、两者的比较
1、主动与被动
Reactor将监听事件放到epoll_wait上进行阻塞,等待事件就绪之后在依次进行同步处理。
Proactor发起I/O事件后立即返回,由内核负责I/O操作,操作完成后用回调函数去进行后续的处理。
可以看出,Reactor被动的等待事件到来并作出反应,他有一个等待的过程,做什么都要先放入到监听事件集合中等待事件就绪才可以处理。而Proactor直接调用异步读写操作,调用完成后立即返回。
2、实现
Reactor实现了一个被动的事件分离和分发模型,服务器等待请求事件的到来,再通过不受间断的同步处理事件,从而做出反应。
Proactor实现了主动的事件分离和事件分发模型,这种设计允许多个任务并发的执行,从而提高吞吐量,并可执行耗时长的任务(各个任务之间互不影响)。
3、优点
Reactor实现相对简单,对于耗时短的场景处理高效,操作系统可以在多个事件源上等待,并且避免了多线程的开销,事件的串行化对应用是透明,可以顺序同步的执行。
Proactor性能更高,能够处理耗时长的并发场景。
4、缺点
Reactor处理耗时长的操作会造成事件分发的阻塞,影响后续事件的处理。
Proactor实现逻辑比较复杂。
5、应用场景
Reactor:同时接收多个服务请求,并依次同步处理他们的事件驱动程序。
Proactor:异步接收和同时处理多个服务请求的事件驱动程序。
6、Reactor模式和Proactor模式区别
Reactor和Proactor最本质的区别就是由谁负责数据的读写。Reactor由用户进行数据的读写,Proactor由内核完成读写操作。
7、同步和异步的区别
同步和异步是相对于应用和内核的交互方式而言的,同步需要主动去询问结果,而异步的时候内核在I/O事件发生之后通知应用程序。即同步是被动查看,异步是主动推送。
四、用Reactor模拟Proactor
1、Proactor的难点
真正的异步模式是需要操作系统支持的,但并不是所有的操作系统都为底层异步提供健壮的支持,Unix/Linux系统对异步有些麻烦。我们可以对Reactor模型做一些调整,模拟异步的Proactor模型。
使用同步I/O模型,模拟出Proactor模型(以epoll_wait为例): 1、主线程向epoll内核事件表中注册socket上的读事件。 2、主线程调用epoll_wait等待socket上有数据可读。 3、当socket上有数据可读时,epoll_wait通知主线程,主线程从socket循环读取数据,直到没有数据可读为止,然后将读取到的数据封装成一个请求对象并插入到请求队列。(这时候读事件已经完毕) 4、睡眠在请求队列上的某个工作线程被唤醒,他将获得请求对象并处理客户请求。处理完毕之后再注册写事件,将处理结果返回给客户。 5、主线程调用epoll_wait等待socket可写。 6、当socket可写时,epoll_wait通知主线程,主线程往socket中写入服务器处理客户请求的结果。