Muduo类详解之EventLoop

简介: Muduo类详解之EventLoop

       最核⼼的部分就是 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();
 // 停⽌ Loop
 void 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_; // ⽤于异步唤醒的 channel
 pid_t thread_id_; // 线程id
 mutable 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. 处理超时事件,到期了就从定时器⼩根堆中删除

相关文章
|
6月前
|
安全 API
muduo源码剖析之EventLoop事件循环类
EventLoop.cc就相当于一个reactor,多线程之间的函数调用(用eventfd唤醒),epoll处理,超时队列处理,对channel的处理。运行loop的进程被称为IO线程,EventLoop提供了一些API确保相应函数在IO线程中调用,确保没有用互斥量保护的变量只能在IO线程中使用,也封装了超时队列的基本操作。
84 0
|
6月前
muduo源码剖析之Socket类
封装了一个sockfd相关的设置。比较简单,已经编写注释。
57 0
|
6月前
muduo源码剖析之Acceptor监听类
Acceptor类用于创建套接字,设置套接字选项,调用socket()->bind()->listen()->accept()函数,接受连接,然后调用TcpServer设置的connect事件的回调。listen()//在TcpServer::start中调用封装了一个listen fd相关的操作,用于mainLoop接受器封装,实质上就是对Channel的多一层封装监听连接 当新连接进入时,调用Socket::accept创建套接字,触发TcpServer的回调TcpServer通过该接口设置回调,
50 0
|
6月前
|
安全 Java
【Netty 网络通信】EventLoop 事件循环对象 解析
【1月更文挑战第9天】【Netty 网络通信】EventLoop 事件循环对象 解析
|
6月前
muduo源码剖析之channel通道类
channel是muduo中的事件分发器,它只属于一个EventLoop,Channel类中保存着IO事件的类型以及对应的回调函数,每个channel只负责一个文件描述符,但它并不拥有这个文件描述符。channel是在epoll和TcpConnection之间起沟通作用,故也叫做通道,其它类通过调用channel的setCallbcak来和建立channel沟通关系。
108 0
|
6月前
|
API
muduo源码剖析之SocketOps类
对socket设置API的封装。比较简单,已经编写注释。
42 0
|
5月前
|
应用服务中间件 Go nginx
Swoole 源码分析之 epoll 多路复用模块
IO多路复用技术通过使用少量的线程或进程同时监视多个IO事件,能够更高效地处理大量的IO操作,从而提高系统的性能和资源利用率。
58 0
Swoole 源码分析之 epoll 多路复用模块
|
6月前
Muduo类详解之Poller
Muduo类详解之Poller
|
6月前
|
网络协议
Muduo类详解之Channel
Muduo类详解之Channel
|
6月前
|
安全 C++ 容器
muduo源码剖析之TimerQueue类
通过timerfd实现的定时器功能,为EventLoop扩展了一系列runAt,runEvery,runEvery等函数TimerQueue中通过std::set维护所有的Timer,也可以使用优先队列实现muduo的TimerQueue是基于timerfd_create实现,这样超时很容易和epoll结合起来。等待超时事件保存在set集合中,注意set集合的有序性,从小到大排列,整个对TimerQueue的处理也就是对set集合的操作。
51 0
muduo源码剖析之TimerQueue类