当前位置: 首页 > news >正文

C++ 并发核心模型总结—— 从阻塞 IO 到 Reactor + 协程的完整理解(附 mini epoll + Reactor demo)

前面学习的内容:
C++ 网络服务端主线:从线程池到 Reactor 的完整路线图

一、为什么要学这套模型?

很多人学 C++ 网络编程,会陷入这些困惑:

  • 为什么线程越多反而越慢?
  • epoll 到底解决什么问题?
  • Reactor 和线程池有什么区别?
  • 协程为什么突然变得很重要?

👉 本质是:

你没有把 IO模型 + 调度模型 + 执行模型打通

本文目标

帮你一次性理解:

  • 阻塞 IO
  • 非阻塞 IO
  • epoll
  • Reactor
  • C++ 协程

并用一个mini Reactor demo把它们串起来。

二、总纲(先记住这5句话)

阻塞IO:线程等数据
非阻塞IO:线程不等数据
epoll:告诉你谁有数据
Reactor:调度谁去处理数据
协程:让代码可以暂停再继续执行

三、阻塞 IO vs 非阻塞 IO


1️⃣ 阻塞 IO(Blocking IO)

read(fd, buffer, size);

👉如果没有数据:线程被挂起(卡住)

👉 特点:

  • 简单 ✔
  • 直观 ✔
  • 占线程 ❌

👉 一句话:

没数据就等(卡线程)

2️⃣ 非阻塞 IO(Non-blocking IO)

fcntl(fd, F_SETFL, O_NONBLOCK); read(fd, buffer, size);

👉如果没有数据:立刻返回(errno = EAGAIN)

👉 特点:

  • 不阻塞线程 ✔
  • 需要配合机制判断何时再读 ❗

👉 一句话:

没数据就走(不等)

四、为什么需要 epoll?

问题

如果只用非阻塞 IO:

while(true) { read(fd); }

👉 会变成:疯狂轮询 → CPU 100%

epoll 的作用

由内核告诉你:哪个 fd “现在可以读/写”

核心调用

epoll_wait(...)

返回:ready 的 fd 列表

👉 一句话:

谁可以干活了

五、Reactor 模型

Reactor 本质

事件驱动调度模型

核心流程

epoll_wait() ↓ 获取 ready fd ↓ 分发 handler ↓ 执行处理逻辑

👉 一句话:

安排谁去干活

关键关系

❗ Reactor ≠ epoll
✔ Reactor 使用 epoll

六、协程(C++20)


协程本质

用户态执行流控制(可暂停/恢复)


核心能力

co_await ...

表示:

执行到一半 → 挂起 → 以后再继续


👉 一句话:

代码可以停下来再继续执行

七、五者关系总图(最重要)

非阻塞 IO

epoll(事件通知)

Reactor(调度)

协程 / handler(执行)

八、mini epoll + Reactor demo(核心实战)

功能

  • 单线程
  • 非阻塞 socket
  • epoll 监听
  • Reactor 分发
  • echo server

✅ 完整代码

#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <sys/epoll.h> #include <sys/socket.h> #include <unistd.h> #include <cstring> #include <functional> #include <iostream> #include <unordered_map> #include <vector> class Reactor { public: using Handler = std::function<void(int)>; Reactor() { epoll_fd_ = epoll_create1(0); } void add(int fd, uint32_t events, Handler handler) { epoll_event ev{}; ev.events = events; ev.data.fd = fd; epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev); handlers_[fd] = handler; } void remove(int fd) { epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr); handlers_.erase(fd); close(fd); } void loop() { std::vector<epoll_event> events(64); while (true) { int n = epoll_wait(epoll_fd_, events.data(), 64, -1); for (int i = 0; i < n; ++i) { int fd = events[i].data.fd; handlers_[fd](fd); } } } private: int epoll_fd_; std::unordered_map<int, Handler> handlers_; }; int set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } int main() { int server_fd = socket(AF_INET, SOCK_STREAM, 0); set_nonblocking(server_fd); 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, 128); Reactor reactor; // accept handler reactor.add(server_fd, EPOLLIN, [&](int fd) { while (true) { int client_fd = accept(fd, nullptr, nullptr); if (client_fd == -1) break; set_nonblocking(client_fd); reactor.add(client_fd, EPOLLIN, [&](int cfd) { char buf[1024]; int n = read(cfd, buf, sizeof(buf)); if (n > 0) { write(cfd, buf, n); } else { reactor.remove(cfd); } }); } }); std::cout << "server running :8080\n"; reactor.loop(); }

九、这个 demo 在做什么?

1️⃣ epoll

epoll_wait()

👉 获取 ready fd

2️⃣ Reactor

handlers_[fd](fd);

👉 分发执行

3️⃣ handler

read / write

👉 执行业务

👉 整体:epoll → Reactor → handler

十、最核心理解(一定要记住)

阻塞 vs 非阻塞

阻塞:线程等
非阻塞:线程不等

epoll vs Reactor

epoll:发现事件
Reactor:调度事件

Reactor vs 协程

Reactor:什么时候执行
协程:执行到哪暂停

十一、最终架构总结

epoll(事件) ↓ Reactor(调度) ↓ 协程 / handler(执行)

阻塞IO让线程等
非阻塞IO让线程不等
epoll告诉你什么时候可以干
Reactor安排谁去干
协程让你干活还能暂停继续

进阶建议

你可以继续进阶:

  • 手写 Reactor + Connection 封装
  • Reactor + 线程池(多 Reactor 模型)
  • Reactor + 协程(接近 Go / Netty)

进阶总纲:

C++ 高性能服务端进阶路线—— 从 epoll + Reactor 到多线程与协程的系统化路径

http://www.jsqmd.com/news/581886/

相关文章:

  • 3个关键步骤构建企业级本地语音合成系统:tts-vue深度解析
  • C++的std--ranges选择管理
  • 心理学知识分享(2026.4.3)
  • 大模型面试宝典(2026版)发布!收藏这份程序员进阶指南,高薪Offer等你拿!
  • 视频获取工具新纪元:N_m3u8DL-CLI-SimpleG全方位解析
  • Stanford CoreNLP:自然语言处理工具包的技术解析与实战指南
  • 牛客网Java面试题总结(金三银四最新版)
  • 开源项目实战部署指南:从环境搭建到应用优化
  • SiameseAOE模型对比实验:与传统规则和词典方法的性能评估
  • 万象视界灵坛惊艳效果:像素风勋章系统动态升级——从‘青铜神谕者’到‘万象先知’的成长路径可视化
  • Go Routine 调度模型详解
  • Go Context 取消机制原理
  • 解锁Zotero插件管理新范式:让学术效率提升300%的实战指南
  • 二次封装ElementUI日期范围组件:打造带限制规则的Vue2 v-model响应式通用组件
  • JX3Toy终极指南:如何用自动化脚本提升剑网3游戏效率300%
  • 3大核心功能打造完美暗黑2角色:d2s-editor存档编辑工具全解析
  • 3分钟解锁音频自由:开源音频处理工具全方位解决方案
  • C++的std--span动态范围与静态范围在API设计中的灵活性选择
  • 番茄小说下载器:5分钟掌握离线阅读终极解决方案
  • 终极游戏自动化指南:如何用JX3Toy实现剑网3全门派DPS优化
  • 量化交易开发实战指南:从入门到部署
  • 凤铝新家装门窗工厂怎么样,从详细介绍看上海选购指南 - 工业品网
  • 从手速焦虑到技术制胜:DamaiHelper如何重塑抢票游戏规则
  • AI 模型微调与再训练实践
  • 《Spring Boot微服务架构下的电商秒杀系统设计与实现》
  • FLUENT 模拟仿真锂离子电池热失控热扩散:探索电池安全的数字钥匙
  • 户外露营装备代工厂哪家靠谱? - 中媒介
  • 一文讲清,精益数据分析是什么意思?精益数据分析的核心价值与实践
  • C++ 服务端进阶(一)—— 从 Reactor 到 Connection:服务骨架设计(工程版)
  • 讲讲2026年哈尔滨靠谱的进口大众专修品牌企业,怎么选择 - myqiye