深入学习 Boost.Asio(二):TCP 编程与多线程模型

系列导航:入门篇 | 进阶篇 | 实战篇 前置知识 阅读本篇前,请确保已理解 入门篇 中的以下概念: io_context 的作用和 run() 执行流程 异步操作的生命周期(发起 → 完成 → handler 执行) post/dispatch 的区别 1. TCP 编程:三步演进 我们通过构建一个 Echo Server(收到什么就回什么),从最简单的同步版本逐步演进到生产级协程版本。 1.1 第一步:同步阻塞版 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 // echo_server_sync.cpp // 编译: g++ -std=c++20 echo_server_sync.cpp -lboost_system -lpthread -o echo // 测试: 另开终端 nc localhost 9999,输入文字会回显 #include <boost/asio.hpp> #include <iostream> using boost::asio::ip::tcp; int main() { boost::asio::io_context ioCtx; // 创建 acceptor:监听 TCP 连接 // 参数:io_context, 绑定地址(IPv4, 端口9999) tcp::acceptor acceptor(ioCtx, tcp::endpoint(tcp::v4(), 9999)); std::cout << "同步 Echo Server 监听端口 9999\n"; while (true) { // accept() 阻塞,直到有客户端连接 tcp::socket socket(ioCtx); acceptor.accept(socket); std::cout << "客户端连接: " << socket.remote_endpoint().address().to_string() << ":" << socket.remote_endpoint().port() << "\n"; // 处理这个连接(阻塞:处理期间无法接受新连接!) boost::system::error_code ec; char buf[1024]; while (true) { // read_some:读取可用的数据(可能只有一部分) size_t n = socket.read_some(boost::asio::buffer(buf), ec); if (ec == boost::asio::error::eof) { std::cout << "客户端断开\n"; break; } if (ec) throw boost::system::system_error(ec); // 将收到的数据原样写回 boost::asio::write(socket, boost::asio::buffer(buf, n)); } } return 0; } 问题:同一时刻只能服务一个客户端。当客户端 A 连接后,客户端 B 必须等 A 断开才能被接受。 ...

May 20, 2026 · 10 min · 2026 words

多线程 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