最核⼼的部分就是 EventLoop 、 Channel 以及 Poller 三个类,其中 EventLoop 可以看作是对业务线程的封装,⽽ Channel 可以看作是对每个已经建⽴连接的封装(即 accept(3) 返回的⽂件描述符)
EventLoop
class EventLoop {
public:typedef std::function<void()> Function;// 初始化poller, event_fd,给 event_fd 注册到 epoll 中并注册其事件处理回调EventLoop();~EventLoop();// 开始事件循环 调⽤该函数的线程必须是该 EventLoop 所在线程,也就是 Loop 函数不能跨线程调⽤void Loop();// 停⽌ Loopvoid StopLoop();// 如果当前线程就是创建此EventLoop的线程 就调⽤callback(关闭连接 EpollDel) 否则就放⼊等待执⾏
函数区void RunInLoop(Function&& func);// 把此函数放⼊等待执⾏函数区 如果当前是跨线程 或者正在调⽤等待的函数则唤醒void QueueInLoop(Function&& func);// 把fd和绑定的事件注册到epoll内核事件表void PollerAdd(std::shared_ptr<Channel> channel, int timeout = 0);// 在epoll内核事件表修改fd所绑定的事件void PollerMod(std::shared_ptr<Channel> channel, int timeout = 0);// 从epoll内核事件表中删除fd及其绑定的事件void PollerDel(std::shared_ptr<Channel> channel);// 只关闭连接(此时还可以把缓冲区数据写完再关闭)void ShutDown(std::shared_ptr<Channel> channel);
代码随想录知识星球
从 EventLoop 的类定义中可以看出,除了⼀些状态量以外,每个 EventLoop 持有⼀个 Poller 的智能指针(对
epoll / poll 的封装),⼀个⽤于 EventLoop 之间通信的 Channel ,⾃⼰的线程 id,互斥锁以及装有等待处理函
数的 vector 。很明显, EventLoop 并不直接管理各个连接的 Channel (⽂件描述符的封装),⽽是通过
Poller 来进⾏的。 EventLoop 中最核⼼的函数就是 EventLoop::Loop() 。bool is_in_loop_thread();
private:// 创建eventfd 类似管道的 进程间通信⽅式static int CreateEventfd();void HandleRead(); // eventfd的读回调函数(因为event_fd写了数据,所以触发
可读事件,从event_fd读数据)void HandleUpdate(); // eventfd的更新事件回调函数(更新监听事件)void WakeUp(); // 异步唤醒SubLoop的epoll_wait(向event_fd中写⼊数据)void PerformPendingFunctions(); // 执⾏正在等待的函数(SubLoop注册EpollAdd连接套接字以
及绑定事件的函数)
private: std::shared_ptr<Poller> poller_; // io多路复⽤ 分发器int event_fd_; // ⽤于异步唤醒 SubLoop 的 Loop 函数中的
Poll(epoll_wait因为还没有注册fd会⼀直阻塞)std::shared_ptr<Channel> wakeup_channel_; // ⽤于异步唤醒的 channelpid_t thread_id_; // 线程idmutable locker::MutexLock mutex_;std::vector<Function> pending_functions_; // 正在等待处理的函数bool is_stop_; // 是否停⽌事件循环bool is_looping_; // 是否正在事件循环bool is_event_handling_; // 是否正在处理事件bool is_calling_pending_functions_; // 是否正在调⽤等待处理的函数
};
从 EventLoop 的类定义中可以看出,除了⼀些状态量以外,每个 EventLoop 持有⼀个 Poller 的智能指针(对epoll / poll 的封装),⼀个⽤于 EventLoop 之间通信的 Channel ,⾃⼰的线程 id ,互斥锁以及装有等待处理函数的 vector 。很明显, EventLoop 并不直接管理各个连接的 Channel (⽂件描述符的封装),⽽是通过 Poller 来进⾏的。 EventLoop 中最核⼼的函数就是 EventLoop::Loop() 。
void EventLoop::Loop() {// 开始事件循环 调⽤该函数的线程必须是该EventLoop所在线程assert(!is_looping_);assert(is_in_loop_thread());is_looping_ = true;is_stop_ = false;while (!is_stop_) {// 1、epoll_wait阻塞 等待就绪事件auto ready_channels = poller_->Poll();is_event_handling_ = true;// 2、处理每个就绪事件(不同channel绑定了不同的callback)for (auto& channel : ready_channels) {channel->HandleEvents();}is_event_handling_ = false;// 3、执⾏正在等待的函数(fd注册到epoll内核事件表)PerformPendingFunctions();// 4、处理超时事件 到期了就从定时器⼩根堆中删除(定时器析构会EpollDel掉fd)poller_->HandleExpire();}is_looping_ = false;
}
每个 EventLoop 对象都唯⼀绑定了⼀个线程,这个线程其实就在⼀直执⾏这个函数⾥⾯的 while 循环,这个 while 循环的⼤致逻辑⽐较简单。就是调⽤ Poller::poll() 函数获取事件监听器上的监听结果。接下来在 Loop ⾥⾯就会调⽤监听结果中每⼀个 Channel 的处理函数 HandlerEvent() 。每⼀个 Channel 的处理函数会 根据 Channel 中封装的实际发⽣的事件,执⾏ Channel 中封装的各事件处理函数。(⽐如⼀个 Channel 发⽣ 了可读事件,可写事件,则这个 Channel 的 HandlerEvent() 就会调⽤提前注册在这个 Channel 的可读事件 和可写事件处理函数,⼜⽐如另⼀个 Channel 只发⽣了可读事件,那么 HandlerEvent() 就只会调⽤提前注册在这个 Channel 中的可读事件处理函数。
从中可以看到,每个 EventLoop 实际上就做了四件事
1. epoll_wait阻塞 等待就绪事件(没有注册其他fd时,可以通过event_fd来异步唤醒)
2. 处理每个就绪事件
3. 执⾏正在等待的函数(fd注册到epoll内核事件表)
4. 处理超时事件,到期了就从定时器⼩根堆中删除