
目录
摘要
EventLoop.hpp 把线程内的任务队列与 Poller 合成一个调度单位:_thread_id 标所属线程,_event_fd 用 eventfd 打断 epoll_wait,_mutex 保护 vector 型 _tasks,另有 _poller 与 _event_channel。Start() 依次 Poll 取活跃集、HandleEvent() 分发、RunAllTask() 清队列。
代码仓库:https://gitee.com/btshadow/Ubuntu_learning/blob/master/October/10-21/muduo/src/server.hpp
注释
3.1 设计点
- 便于管理内部数据为先,结构开销其次,故任务队列用 vector 而非 queue。2. 本模块做的是「线程单位内」与「任务队列」的调度,集成 Poller、Channel、
_tasks(临界资源):2.1 因在线程内,故有_thread_id标识所属线程;2.2 因 Poller 会异步阻塞,故用 eventfd 防未就绪事件一直阻塞;2.3 因有_tasks,必须用 mutex 保护临界资源;2.4 因要维护 eventfd 的读写,才引入 Channel;2.5 引入 Poller 只为管理 fd 的 EPOLL 系列。3. 超时管理暂未纳入。
3.2 误区
unique_ptr 等智能指针一定先调智能指针自身的构造,之后才决定是否调指向类型的构造,优先顺序别搞混。
3.3 新知识
- 勿穷究,找适当:
_event_channel只管 eventfd 的读、写及 CallBack,别把它的 Poller 与EventLoop::_poller综合来看,不必追求完美。2. 看完一个模块再下手写,切勿提早写,重要⚠️。
正文
构造先取 _thread_id,再 eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK),失败即 abort(),随后 make_unique<Channel>(_event_fd, this)------旧写法传的是 &_poller,行内注释说明任务要跨线程故传 this;EnableRead() 内部已自动 Update,末尾 SetReadCB。ReadEventfd 读出 uint64_t 消耗累加值,WeakUpEventfd 写入 1。Push 在锁内 push_back 后调 WeakUpEventfd(行内 TODO 存疑);PushTaskqueue 同线程直跑,否则入队;RunAllTask 锁内 swap 到临时 vector、出锁逐个执行;析构仅把 _event_fd = -1。
bug1:gcc 无法定位 ReadEventfd 函数的「位置」。先写 std::bind(ReadEventfd, this),再改 std::bind(EventLoop::ReadEventfd, this) 仍不行;取成员函数指针必须用 &类名::函数名,& 不能省略,故最终为 std::bind(&EventLoop::ReadEventfd, this)。
总结
取舍是管理优先于结构开销:vector 作队列、swap 后出锁执行、eventfd 打通阻塞;bug1 属成员函数指针语法。
常见问题
_tasks为何用 vector?见 3.1 第 1 点。RunAllTask为何 swap 后出锁执行?执行在锁外,不持锁跑业务。- bug1 的
&加在哪?SetReadCB中 bind 的首参。
标签
C++、epoll、eventfd、任务队列、成员函数指针
【统计:全文约 700 字 / 原代码 154 行 / 比例约 4.5;注释字段小节 3 个 / 成文 bug 1 处】(字数不含代码块与头部图片、目录标记)