cpp-tbox项目链接 https://gitee.com/cpp-master/cpp-tbox
更多精彩内容欢迎关注微信公众号:码农练功房
往期精彩内容:
Linux应用框架cpp-tbox之弱定义
Linux应用框架cpp-tbox之日志系统设计
什么是Reactor模式
Reactor模式是一种事件驱动的设计模式,广泛应用于异步I/O处理,特别是在需要高效处理大量并发连接的服务器程序中,如Web服务器、数据库服务器、即时通讯软件等。
Reactor模式的核心组件是一个事件循环(EventLoop)线程,它负责监听、分发并处理来自I/O源的事件。
cpp-tbox采用的也是Reactor模式,为了便于我们理解什么是Reactor模式,库作者给出过一个贴近生活的例子:
Reactor线程就像是一个银行的办事柜台。如果遇到很轻松就能完成的事务,比如查询余额,柜台工作人员就立即处理了。
如果遇到的是比较繁重的工作,比如大额的取款,柜台工作人员便令后面的工作人员进行操作,让顾客在休息区等待。
柜台工作人员则继续接待其它的顾客。
等后面的工作人员取出了大金额的现金后,柜台工作人员呼叫取钱的顾客,并将现金给到该顾客。
这个过程中,柜台工作人员就是Reactor线程,后面的工作人员就是线程池的工作线程。
查询余额则是非阻塞性任务,取大额现金则是阻塞性的任务。
在 tbox.main 框架编程中,一切都是基于事件驱动的。具体操作就是:向Reactor注册某个事件的回调函数。当该事件发生了,Reactor就会回调之前注册的函数。 这种模型对注册的回调函数有三个基本的要求:不要阻塞!不要阻塞!不要阻塞!。
Reactor模式关键组件
以下是基于Reactor模式EventLoop编程的基本组成部分:
- 事件分离器(Event Demultiplexer): 在大多数Unix-like系统中,可以是select、poll、epoll或kqueue等IO多路复用机制。它的作用是监控多个I/O事件源(如文件描述符),并告知哪些是就绪的(可读、可写或异常)。
- 事件处理器(Event Handler): 一个或多个处理器,负责具体的事件处理逻辑。每个事件(如新连接请求、数据可读取、数据写入完成)都有对应的处理器。
- 事件循环(Event Loop): 核心组件,不断地执行以下循环:
- 调用事件分离器等待就绪事件;
- 分发就绪事件给相应的事件处理器;
- 执行处理器中的回调函数或任务;
- 重复上述过程。
- 同步队列(Synchronization Queue): 可选组件,用于存放待处理的任务或事件,帮助管理并发和同步。
整体结构图
下图是cpp-tbox中EventLoop的代码模型:
其中Loop对象的创建、释放,由ContexImp对象负责管理。
Loop是一个抽象接口,用于屏蔽不同IO多路复用机制,CommonLoop则是对公共代码进行了提取,EpollLoop是对epoll的封装。
FdEvent封装了文件描述符,用于处理IO事件;SignalEvent和TimerEvent则分别对信号、定时器进行了封装。
这三类事件对象都由Loop提供对象创建接口,但是他们的生命周期由上层来管理。这三类事件先按下不表,我们此次着重Loop的设计。
runLoop
runLoop所在的线程也叫做IO线程,主要用来监控多个I/O事件源。
// cpp-tbox\modules\event\engines\epoll\loop.cpp
void EpollLoop::runLoop(Mode mode)
{
if (epoll_fd_ < 0)
return;
std::vector<struct epoll_event> events;
events.resize(max_loop_entries_);
runThisBeforeLoop();
keep_running_ = (mode == Loop::Mode::kForever);
do {
int fds = epoll_wait(epoll_fd_, events.data(), events.size(), getWaitTime());
beginLoopProcess();
handleExpiredTimers(); // 处理定时器事件
for (int i = 0; i < fds; ++i) {
epoll_event