C++ 高性能网络服务骨架(二)—— 线程池接入:accept 与 worker 分离

张开发
2026/4/8 16:22:46 15 分钟阅读

分享文章

C++ 高性能网络服务骨架(二)—— 线程池接入:accept 与 worker 分离
这一篇要解决什么问题在第一篇里我们实现了一个最简单的 TCP 服务端accept → read → write → close问题是❌所有请求串行执行也就是说一个慢请求 → 卡住后面所有请求服务端几乎没有并发能力本篇目标把模型升级为主线程 accept 新连接 ↓ 把连接丢给线程池 ↓ worker 线程处理请求 核心变化accept 和请求处理解耦一、整体架构图非常重要客户端 │ ▼ ┌────────────┐ │ 主线程 │ ← 只负责 accept └────────────┘ │ ▼ ┌────────────┐ │ 线程池队列 │ └────────────┘ │ ▼ ┌────────────┐ │ worker线程 │ ← read / write / close └────────────┘二、为什么必须这样拆❌ 错误模型第一篇accept → read → write → close 一个连接没处理完accept 就不能继续✅ 正确模型本篇accept → 任务入队 → worker处理 主线程永远在“接活”三、核心思想一定要记住线程池不是工具而是服务端的执行引擎分工模型角色职责主线程accept 连接线程池调度任务worker线程处理请求四、核心改造点我们只改一件事第一篇代码int client_fd accept(...); // 直接处理 read(client_fd, ...); write(client_fd, ...); close(client_fd);改成int client_fd accept(...); // 丢给线程池 pool.submit([client_fd]() { // read / write / close }); 就这一行完成架构升级。五、完整代码线程池接入版 假设你已经有 ThreadPool上一章写的#include iostream #include cstring #include unistd.h #include arpa/inet.h #include ThreadPool.h // 你之前实现的线程池 void handle_client(int client_fd) { char buffer[1024] {0}; read(client_fd, buffer, sizeof(buffer)); std::cout Received:\n buffer std::endl; const char* response HTTP/1.1 200 OK\r\n Content-Length: 13\r\n \r\n Hello, World!; write(client_fd, response, strlen(response)); close(client_fd); } int main() { // 1. 创建线程池 ThreadPool pool(4, 100); // 2. 创建 socket int server_fd socket(AF_INET, SOCK_STREAM, 0); sockaddr_in addr{}; addr.sin_family AF_INET; addr.sin_port htons(8080); addr.sin_addr.s_addr INADDR_ANY; bind(server_fd, (sockaddr*)addr, sizeof(addr)); listen(server_fd, 10); std::cout Server running on port 8080... std::endl; while (true) { sockaddr_in client_addr{}; socklen_t len sizeof(client_addr); int client_fd accept(server_fd, (sockaddr*)client_addr, len); // ⭐ 核心丢给线程池 pool.submit([client_fd]() { handle_client(client_fd); }); } return 0; }六、流程再走一遍必须脑子有图客户端请求 ↓ 主线程 accept ↓ 得到 client_fd ↓ submit 到线程池 ↓ worker 线程执行 handle_client ↓ read → write → close七、关键细节面试必问1️⃣ 为什么 client_fd 可以传给线程池pool.submit([client_fd]() { ... }); 因为fd 是一个 int值拷贝每个连接独立2️⃣ 为什么必须在 worker 里 close 因为谁用谁关闭生命周期归 worker3️⃣ 主线程为什么不能 read 因为会阻塞 accept4️⃣ 这个模型算高并发吗 ❌ 不算因为还是阻塞 IO每个连接占一个线程八、这个版本的瓶颈问题 1线程数量有限1000连接 → 需要1000线程 ❌问题 2read 是阻塞的客户端慢 → worker线程卡住问题 3上下文切换开销大 所以这个模型只是过渡版本九、这一篇真正的价值不是“并发更快了”而是你第一次真正建立了这个认知accept ≠ 处理请求 服务端的本质是连接接入层 执行层分离十、对标 Java再强化一次Java 写法ExecutorService pool ... while (true) { Socket client server.accept(); pool.submit(() - { handle(client); }); } 一模一样。十一、你现在完成了什么你已经✔ 从“单线程服务端” → “并发服务端”✔ 从“同步模型” → “任务调度模型”✔ 从“写代码” → “搭架构”十二、下一步关键升级现在的问题是❌ 一个连接仍然占一个线程下一篇《C 高性能网络服务骨架三》—— 请求分发、连接管理与服务骨架设计下一篇会解决请求解析不是 raw read响应封装错误处理日志体系连接生命周期管理总结这一篇你一定要记住一句话线程池 服务端执行引擎主线程只负责接连接worker 线程负责干活

更多文章