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. 处理超时事件,到期了就从定时器⼩根堆中删除

相关文章
|
XML JSON 缓存
小红书获得小红书笔记详情 API 返回值说明
-- 请求示例 url 默认请求参数已经URL编码处理 curl -i &quot;https://api-gw.onebound.cn/smallredbook/item_get_video/?key=&lt;您自己的apiKey&gt;&secret=&lt;您自己的apiSecret&gt;&num_iid=649c46ab000000002702ad36&quot;
|
设计模式 架构师 大数据
我的架构师之路——阿里云 MVP 沈剑
沈剑,公众号“架构师之路”的作者,曾任百度高级工程师和58同城高级架构师、技术委员会主席、技术学院优秀讲师,现为到家集团技术委员会主席和技术VP,同时也是快狗打车(原58速运)的CTO。本文是沈剑老师在阿里云的直播中分享的一些自己关于架构师的看法和成为架构师的心路历程的第一部分。
7578 0
我的架构师之路——阿里云 MVP 沈剑
|
存储 Shell Android开发
Android--adb命令查看第三方应用包名、应用activity名
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/78038767 (adb s...
5836 0
|
缓存 移动开发 JavaScript
uniapp H5 公众号微信自定义分享qq,微信带图片标题内容
uniapp H5 公众号微信自定义分享qq,微信带图片标题内容
1801 0
uniapp H5 公众号微信自定义分享qq,微信带图片标题内容
|
XML 前端开发 Java
若依管理系统后端将 Mybatis 升级为 Mybatis-Plus
若依管理系统后端将 Mybatis 升级为 Mybatis-Plus
2361 0
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
564 12
|
Java C++ 开发者
muduo网络库
【6月更文挑战第15天】
789 7
|
数据采集 存储 传感器
物联网的感知层、网络层与应用层分享
物联网的感知层、网络层与应用层分享
2075 1
|
存储 人工智能 BI
数字信号处理-05- FPGA常用运算模块-复数乘法器(二)
数字信号处理-05- FPGA常用运算模块-复数乘法器
1275 0
数字信号处理-05- FPGA常用运算模块-复数乘法器(二)
|
Go C语言 Windows
【教程】使用 IDA Pro + 010 Editor 反编译和修改 so 文件,让你轻松玩转逆向工程!
当我们在做逆向工作时,总会遇到需要查看某个 so 文件的情况,甚至可能需要对其进行一些微小的修改。这时候,我们就需要来点反编译的魔法了。
14037 0
【教程】使用 IDA Pro + 010 Editor 反编译和修改 so 文件,让你轻松玩转逆向工程!