多线程 EventLoop — EventLoopThread & EventLoopThreadPool

第 13 课:多线程 EventLoop — EventLoopThread & EventLoopThreadPool 对应源文件: trantor/net/EventLoopThread.h / EventLoopThread.cc — 在独立线程运行一个 EventLoop trantor/net/EventLoopThreadPool.h / EventLoopThreadPool.cc — EventLoop 线程池 一、为什么需要这两个类? 在第 12 课里,我们看到 TcpServer::setIoLoopNum(4) 内部创建了一个 EventLoopThreadPool。它们解决的核心问题是: 如何安全地在新线程里创建 EventLoop,并确保 EventLoop 真正开始运行后再返回给调用者? 这看起来简单,实际上有一个微妙的同步问题: EventLoop 对象必须在它将要运行的线程里创建(t_loopInThisThread 线程局部变量) 调用者拿到 EventLoop * 之前,要保证该指针有效(对象已创建) run() 返回之前,要保证 EventLoop 确实进入了 loop() 主循环(否则第一个 runInLoop 可能无法立刻执行) trantor 用三个 std::promise 精确解决了这个三阶段同步问题。 二、EventLoopThread 的三阶段启动协议 2.1 成员变量一览 1 2 3 4 5 6 7 8 std::shared_ptr<EventLoop> loop_; // EventLoop 对象(新线程里创建) std::mutex loopMutex_; // 保护 loop_ 的读写(析构时用) std::string loopThreadName_; // 线程名(prctl 设置) std::promise<std::shared_ptr<EventLoop>> promiseForLoopPointer_; // ① EventLoop 指针就绪 std::promise<int> promiseForRun_; // ② "请开始循环"信号 std::promise<int> promiseForLoop_; // ③ "循环已在运行"确认 std::once_flag once_; // 保证 run() 只执行一次 std::thread thread_; // 实际的 OS 线程 2.2 三阶段时序图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 主线程 新线程(loopFuncs) │ │ │ EventLoopThread(name) │ │ → thread_ = std::thread(loopFuncs) │ 线程启动 │ → f = promiseForLoopPointer_.get_future() │ │ ↓ 阻塞等待 ① │ prctl(PR_SET_NAME) │ │ loop = make_shared<EventLoop>() │ 【①】│ promiseForLoopPointer_.set_value(loop) │ ← f.get() 返回 loop 指针 │ │ → this->loop_ = loop │ promiseForLoop_: queueInLoop 注册回调 │ │ f2 = promiseForRun_.get_future() │ (构造完成,loop_ 有效,但循环未开始) │ ↓ 阻塞等待 ② │ │ │ run() │ │ → std::call_once { │ │ f3 = promiseForLoop_.get_future() │ │ 【②】promiseForRun_.set_value(1) │ │ ↓ 阻塞等待 ③ │ ← f2.get() 返回 │ │ loop->loop() 开始 │ │ → 第一次 poll │ │ → doRunInLoopFuncs() │ 【③】│ → promiseForLoop_.set_value(1) │ ← f3.get() 返回 │ (循环持续运行...) │ } │ (run() 返回,EventLoop 确保在运行中) 三个 promise 的职责: ...

March 28, 2025 · 9 min · 1757 words