最近在网上看到一个简易事件反应堆的实现:
https://github.com/song0071000/Reactor_Implemention 学习了下。
- 反应堆的作用
在编写网络程序时,需要处理网络的读写事件、定时器事件等。网络事件的收集通常采用select、epoll等IO多路复用技术,定时器通常采用小顶堆实现。这些通常都是不变的,变化的是事件发生后的逻辑处理。反应堆的作用就是把事件的收集和事件发生后的处理分开,使得开发人员只需关注后者。
下图是一个反应堆的设计,图中的左半部分是反应堆,右半部分是事务处理。这个反应堆可以处理网络和定时器两类事件。
其中,EventHandler
类是事件处理的基类,它有读、写、处理错误等函数。我们只需继承这个类,然后完成读写逻辑,然后将其注册到反应堆中即可。 - 反应堆的实现
其中,Reactor
类是反应堆的基类,它有注册事件、删除事件、处理事件等功能。在它依赖的类中,有个map<handle_t, EventHandler *> m_handlers
成员变量。EventHandler
事件注册时,就放在这个变量中,事件删除时,就从这个变量中移除。
比如,我们要监听一个socket,继承EventHandler
,然后将socket的handle和事件类注册到反应堆中。
注册是通过Reactor
类型的全局变量g_reactor
调用RegisterHandler(EventHandler * handler, event_t evt)
函数。由于底层是采用select或epoll,注册就是采用select或epoll监听这些socket。当有事件发生时,调用EventHandler
的事件处理函数处理读写。 - 定时器
定时器的实现是通过一个小顶堆time_heap
,然后在反应堆中有个成员变量time_heap* m_eventtimer
。这个类有添加定时器、移除定时器、处理定时器等功能。添加定时器就是在小顶堆中添加一个heap_timer
事件,这个事件中有过期时间和回调函数。
反应堆处理事件的过程的示意代码1
2
3
4
5
6
7
8
9
10
11while(1)
{
// 获取定时器最小过期时间
timeout = get_min_expire_from(time_heap);
// 从监听的sockets中获取活跃的socket
ready_sockets = select_from(m_handlers, timeout);
// 处理socket的网络事件
HandleEvents(ready_sockers);
// 处理过期的定时事件
HandleTimes(time_heap);
}
由此可见,事件反应堆将事件的处理从一个主动过程变成一个被动过程:我们只需将想要处理的事件注册到反应堆上,事件发生时由反应堆去处理,而不是我们主动处理。