目录一、 Poller.h1. Poller 类的整体定位与核心设计目标2. 核心成员变量解析1. 私有成员所属 EventLoop线程安全核心2. 保护成员fd 到 Channel 的映射核心管理结构3. 公共类型别名活跃 Channel 列表3. 核心接口逐模块拆解1. 构造 / 析构绑定 EventLoop 抽象析构2. 核心纯虚函数IO 多路复用核心接口子类必须实现3. 辅助接口检查 Channel 是否存在4. 静态工厂方法创建默认 Poller多态核心5. 线程安全校验强制在 Loop 线程执行4. 核心设计亮点总结二、 Poller.h1. 代码整体核心逻辑回顾2. 逐函数拆解核心实现1. 构造函数绑定所属 EventLoop线程安全基础2. 析构函数默认析构基类无资源管理3. hasChannel校验 Channel 是否在 Poller 管理范围内核心1. 线程安全校验assertInLoopThread()2. fd 查找channels_.find(channel-fd())3. 指针一致性验证it-second channel3. 核心设计亮点总结总结一、 Poller.h先贴出完整代码再逐部分解释// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // 本源代码的使用受 BSD 风格许可证约束 // 该许可证可在 License 文件中查阅。 // 作者陈硕 (chenshuo at chenshuo dot com) // // 这是一个内部头文件你不应该直接包含它。 #ifndef MUDUO_NET_POLLER_H #define MUDUO_NET_POLLER_H #include map #include vector #include muduo/base/Timestamp.h // 时间戳类记录 poll 返回的时间 #include muduo/net/EventLoop.h // EventLoop 前向声明Poller 所属的事件循环 namespace muduo { namespace net { class Channel; // 前向声明Poller 管理的事件通道类 /// /// IO 多路复用的抽象基类 /// /// 核心特性1. 非拷贝禁止拷贝 Poller 实例2. 不持有 Channel 对象的所有权 /// 3. 所有接口必须在所属 EventLoop 的线程中调用4. 定义 IO 多路复用的核心接口 /// 由派生类如 EpollPoller/PollPoller实现具体的 epoll/poll 逻辑 class Poller : noncopyable { public: /// 活跃 Channel 列表类型存储触发事件的 Channel 指针 typedef std::vectorChannel* ChannelList; /// 构造函数绑定所属的 EventLoop /// param loop Poller 所属的 EventLoop一个 Poller 仅属于一个 EventLoop Poller(EventLoop* loop); /// 虚析构函数确保派生类析构时正确调用自身析构函数 virtual ~Poller(); /// 轮询等待IO 事件 /// 核心约束必须在 EventLoop 所属线程调用 /// param timeoutMs 超时时间毫秒-1无限等待0非阻塞 /// param activeChannels 输出参数存储触发事件的活跃 Channel 列表 /// return 实际轮询返回的时间戳用于计算事件处理耗时 virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) 0; /// 更新 Channel 关注的 IO 事件注册/修改事件 /// 核心约束必须在 EventLoop 所属线程调用 /// param channel 要更新的 Channel 对象Poller 不持有其所有权 virtual void updateChannel(Channel* channel) 0; /// 移除 Channel取消所有事件关注并删除映射 /// 调用时机Channel 析构时 /// 核心约束必须在 EventLoop 所属线程调用 /// param channel 要移除的 Channel 对象 virtual void removeChannel(Channel* channel) 0; /// 检查 Poller 是否管理指定的 Channel /// param channel 要检查的 Channel 对象 /// return true已管理false未管理 virtual bool hasChannel(Channel* channel) const; /// 工厂方法创建默认的 Poller 实例根据系统环境选择 EpollPoller 或 PollPoller /// param loop Poller 所属的 EventLoop /// return 指向 Poller 派生类实例的指针 static Poller* newDefaultPoller(EventLoop* loop); /// 断言当前线程是 Poller 所属 EventLoop 的线程保证线程安全 /// 若断言失败程序终止并输出错误信息 void assertInLoopThread() const { ownerLoop_-assertInLoopThread(); } protected: /// Channel 映射表fd → Channel*快速通过文件描述符找到对应的 Channel /// 核心作用避免遍历所有 Channel提升事件分发效率 typedef std::mapint, Channel* ChannelMap; ChannelMap channels_; private: /// Poller 所属的 EventLoop一个 Poller 绑定唯一的 EventLoop EventLoop* ownerLoop_; }; } // namespace net } // namespace muduo #endif // MUDUO_NET_POLLER_H1. Poller 类的整体定位与核心设计目标Poller是 Muduo Reactor 模型中IO 多路复用层的抽象基类核心设计目标接口统一为 epoll/poll/kqueue 等不同 IO 多路复用实现定义统一的抽象接口上层EventLoop无需关心底层具体用哪种多路复用器解耦 EventLoop 与多路复用实现EventLoop 仅依赖 Poller 抽象接口可无缝切换 epoll/poll 等实现符合 “开闭原则”线程安全约束所有操作必须在 EventLoop 所属线程执行通过assertInLoopThread强制校验非持有设计仅管理 fd 到 Channel 的映射channels_不持有 Channel 对象的生命周期Channel 由上层如 TcpConnection/Acceptor 管理。在 Muduo 架构中Poller 是 EventLoop 的 “核心依赖”EventLoop → 持有 Poller 对象 → Pollerepoll/poll监听 fd 事件 → 触发 Channel::handleEventEventLoop 调用 Poller 的poll方法等待 IO 事件Poller 填充活跃 Channel 列表后EventLoop 遍历执行 Channel 的事件回调。2. 核心成员变量解析1. 私有成员所属 EventLoop线程安全核心private: EventLoop* ownerLoop_; // Poller 所属的 EventLoop一个 Poller 仅属于一个 EventLoop核心作用通过ownerLoop_-assertInLoopThread()确保所有 Poller 操作都在 EventLoop 线程执行避免多线程操作导致的状态混乱生命周期绑定Poller 由 EventLoop 创建与 EventLoop 同生命周期。2. 保护成员fd 到 Channel 的映射核心管理结构protected: typedef std::mapint, Channel* ChannelMap; ChannelMap channels_; // fd → Channel* 的映射管理所有关注的 Channel设计逻辑每个 fd 对应一个 ChannelPoller 通过 fd 快速找到对应的 Channel在事件发生时调用 Channel::handleEvent访问权限protected允许子类如 EPollPoller/PollPoller直接访问提升操作效率非持有语义channels_中存储的是 Channel 指针Poller 不负责 Channel 的创建 / 析构仅做 “关联映射”。3. 公共类型别名活跃 Channel 列表public: typedef std::vectorChannel* ChannelList; // 活跃 Channel 列表类型作用poll方法的输出参数类型Poller 等待到 IO 事件后将触发事件的 Channel 指针填充到该列表供 EventLoop 遍历处理。3. 核心接口逐模块拆解1. 构造 / 析构绑定 EventLoop 抽象析构Poller(EventLoop* loop); // 构造绑定所属的 EventLoop virtual ~Poller(); // 虚析构确保子类析构时调用正确的析构函数虚析构必要性Poller 是抽象基类子类如 EPollPoller通过基类指针析构时必须调用子类析构函数避免内存泄漏构造约束Poller 必须与一个 EventLoop 绑定且生命周期由 EventLoop 管理。、2. 核心纯虚函数IO 多路复用核心接口子类必须实现这是 Poller 作为抽象基类的核心定义了所有 IO 多路复用器必须实现的 “契约”/// 等待 IO 事件超时时间 timeoutMs毫秒填充活跃 Channel 列表 /// 必须在 Loop 线程调用返回值为 Poller 唤醒的时间戳用于 EventLoop 计时 virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) 0; /// 更新 Channel 的关注事件新增/修改 /// 必须在 Loop 线程调用如 Channel::enableReading 最终调用此方法 virtual void updateChannel(Channel* channel) 0; /// 移除 Channel停止监听该 fd 的事件 /// 必须在 Loop 线程调用如 Channel::remove 最终调用此方法 virtual void removeChannel(Channel* channel) 0;纯虚函数意义强制子类EPollPoller/PollPoller实现具体逻辑保证接口统一线程约束所有方法必须在 EventLoop 线程执行避免多线程修改 Poller 状态如 epoll_ctl 并发调用。3. 辅助接口检查 Channel 是否存在virtual bool hasChannel(Channel* channel) const;核心逻辑遍历channels_检查是否存在channel-fd()对应的映射返回 bool应用场景EventLoop 析构时校验是否所有 Channel 已移除避免泄漏。4. 静态工厂方法创建默认 Poller多态核心static Poller* newDefaultPoller(EventLoop* loop);核心作用工厂模式根据系统环境创建默认的 Poller 实现Linux 下优先创建 EPollPoller其他系统如 BSD 下创建 KQueuePoller无 epoll/kqueue 则创建 PollPoller上层价值EventLoop 只需调用newDefaultPoller无需关心底层用 epoll 还是 poll实现 “接口与实现分离”。5. 线程安全校验强制在 Loop 线程执行void assertInLoopThread() const { ownerLoop_-assertInLoopThread(); // 调用 EventLoop 的线程校验方法 }实现逻辑EventLoop::assertInLoopThread会检查当前线程是否是 EventLoop 的创建线程不是则终止进程核心价值通过断言强制所有 Poller 操作在 Loop 线程执行避免多线程操作 epoll fd 等导致的未定义行为。4. 核心设计亮点总结设计点解决的问题价值抽象基类 纯虚函数不同 IO 多路复用器epoll/poll接口不统一上层需适配定义统一契约上层EventLoop无需关心具体实现可无缝切换多路复用器非持有 Channel 设计Poller 持有 Channel 会导致生命周期混乱如 Channel 析构时 Poller 还引用仅管理 fd 映射Channel 生命周期由上层掌控解耦资源管理线程安全强制校验多线程调用 epoll_ctl 等系统调用导致状态混乱断言确保所有操作在 Loop 线程执行避免并发问题静态工厂方法newDefaultPoller上层需手动选择 Poller 实现耦合度高工厂模式隐藏实现细节适配不同系统符合 “开闭原则”fd → Channel 映射ChannelMap事件发生时无法快速找到对应的 ChannelO (1) 时间通过 fd 找到 Channel提升事件分发效率二、 Poller.h先贴出完整代码再逐部分解释// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // 本源代码的使用受 BSD 风格许可证约束 // 该许可证可在 License 文件中查阅。 // 作者陈硕 (chenshuo at chenshuo dot com) #include muduo/net/Poller.h #include muduo/net/Channel.h // Channel 类获取 fd、校验指针 using namespace muduo; using namespace muduo::net; /// 构造函数初始化 Poller 所属的 EventLoop /// param loop Poller 绑定的 EventLoop一个 Poller 仅属于一个 EventLoop Poller::Poller(EventLoop* loop) : ownerLoop_(loop) // 初始化成员变量绑定所属的 EventLoop { } /// 析构函数使用默认实现 /// 原因Poller 是抽象基类无需要手动释放的资源channels_ 是 std::map自动析构 /// 派生类如 EpollPoller的析构逻辑由自身实现基类无需处理 Poller::~Poller() default; /// 检查 Poller 是否管理指定的 Channel 对象 /// 核心逻辑1. 校验调用线程必须是 EventLoop 线程2. 通过 fd 查找 Channel /// 3. 校验找到的 Channel 指针与传入指针一致防止 fd 复用导致的错误匹配 /// param channel 要检查的 Channel 对象指针 /// return truePoller 正在管理该 Channelfalse未管理 bool Poller::hasChannel(Channel* channel) const { assertInLoopThread(); // 断言当前线程是 Poller 所属 EventLoop 的线程保证线程安全 // 1. 通过 Channel 的 fd 在 channels_ 映射表中查找 ChannelMap::const_iterator it channels_.find(channel-fd()); // 2. 双重校验 // - 查找结果不为 endfd 存在于映射表 // - 映射表中存储的 Channel 指针与传入指针完全一致避免 fd 复用导致的误判 return it ! channels_.end() it-second channel; }1. 代码整体核心逻辑回顾Poller.cpp是 Poller 抽象基类的基础实现仅包含无业务逻辑的通用接口构造函数完成与 EventLoop 的绑定是 Poller 线程安全的基础析构函数使用默认析构 default因为基类无需要手动释放的资源子类如 EPollPoller 会管理自身资源如 epoll fdhasChannel核心是 “线程校验 fd 查找 指针一致性验证”确保 fd 与 Channel 的映射关系准确避免 fd 复用导致的错误。2. 逐函数拆解核心实现1. 构造函数绑定所属 EventLoop线程安全基础Poller::Poller(EventLoop* loop) : ownerLoop_(loop) // 初始化将Poller绑定到指定的EventLoop { }核心设计点极简实现仅完成ownerLoop_的赋值确保一个 Poller 唯一归属一个 EventLoop生命周期约束Poller 由 EventLoop 创建ownerLoop_指向创建它的 EventLoop后续所有操作的线程校验都依赖这个指针无其他逻辑基类不涉及任何具体的多路复用资源如 epoll fd这些都由子类如 EPollPoller在构造时初始化。2. 析构函数默认析构基类无资源管理Poller::~Poller() default;关键设计原因抽象基类无资源需要释放Poller 仅管理channels_fd→Channel* 映射但channels_存储的是指针不持有 Channel 生命周期且无其他系统资源如 epoll fd、poll fd—— 这些资源由子类如 EPollPoller管理子类会实现自己的析构函数如关闭 epoll fd default优势显式使用默认析构既符合 C11 规范又明确表示基类无需手动处理析构逻辑避免冗余代码。3. hasChannel校验 Channel 是否在 Poller 管理范围内核心bool Poller::hasChannel(Channel* channel) const { // 第一步线程安全校验——确保调用在EventLoop线程执行 assertInLoopThread(); // 第二步根据Channel的fd查找映射 ChannelMap::const_iterator it channels_.find(channel-fd()); // 第三步双重验证——不仅要找到fd还要确保映射的Channel指针与传入的一致 return it ! channels_.end() it-second channel; }核心逻辑拆解1. 线程安全校验assertInLoopThread()调用ownerLoop_-assertInLoopThread()检查当前线程是否是 EventLoop 的创建线程如果不是会触发断言失败并终止进程必要性channels_是非线程安全的 std::map仅能在 EventLoop 线程操作避免多线程并发修改 / 查找导致的容器错乱。2. fd 查找channels_.find(channel-fd())通过 Channel 持有的 fd在channels_fd→Channel* 映射中查找对应的条目如果find返回channels_.end()说明该 fd 未被 Poller 管理直接返回 false如果找到 fd 条目还需要执行第三步验证关键。3. 指针一致性验证it-second channel这是最容易被忽略但至关重要的一步目的是解决「fd 复用」问题场景举例fd 5 原本绑定到 Channel AChannel A 被移除后fd 5 被关闭并重新分配给新的 Channel B此时channels_.find(5)可能找到旧的映射如果未及时删除或新的映射Channel B必须验证指针是否与传入的 Channel 一致价值确保返回的 “fd 存在” 是「指定的 Channel」对应的 fd而非复用的 fd避免将事件分发到错误的 Channel。3. 核心设计亮点总结设计点解决的问题价值构造函数仅绑定 EventLoop基类无需关心具体多路复用实现聚焦通用逻辑符合 “单一职责”基类只做抽象和通用管理子类做具体实现默认析构函数基类无资源需要释放避免冗余的空析构简化代码明确基类的资源管理边界hasChannel 的双重验证fd 指针fd 复用导致的 “fd 存在但 Channel 不匹配” 错误保证映射关系的准确性避免事件分发到错误的 Channel强制线程校验多线程操作非线程安全的 ChannelMap从源头避免并发问题保证 Poller 操作的线程安全总结构造核心Poller 构造仅绑定所属 EventLoop是后续线程安全校验的基础析构设计基类用默认析构因为无资源需要释放子类负责自身资源如 epoll fd的清理hasChannel 关键先校验线程再查找 fd最后验证 Channel 指针一致性 —— 既保证线程安全又避免 fd 复用导致的映射错误极简原则基类仅实现通用逻辑不涉及具体多路复用操作符合抽象基类的设计初衷。