1. 从阻塞IO到Reactor+协程的演进之路
作为一名长期奋战在C++服务端开发一线的工程师,我见证了太多开发者在这个领域的困惑与挣扎。网络编程就像一座迷宫,而阻塞IO、非阻塞IO、epoll、Reactor和协程就是指引我们走出迷宫的关键路标。让我用最接地气的方式,带你看清这个技术演进的完整脉络。
十年前我刚入行时,处理网络请求的第一反应就是"一个连接一个线程"的阻塞IO模式。这种模式简单直观,就像快餐店的"一个顾客一个服务员"——每个线程全程服务一个连接,从点餐到上菜都不换人。但随着并发量上升,问题立刻显现:线程切换开销呈指数级增长,1000个连接需要1000个线程,CPU把大量时间花在线程切换上,实际处理请求的时间反而被挤压。
关键认知:线程不是越多越好,当线程数超过CPU核心数时,线程切换的开销会抵消并发带来的收益
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 阻塞IO与非阻塞IO的本质区别
2.1 阻塞IO的工作机制
cpp复制// 典型阻塞IO调用
char buf[1024];
int n = read(fd, buf, sizeof(buf)); // 线程在这里卡住直到数据就绪
阻塞IO的核心特点是同步等待。当线程执行read操作时,如果内核缓冲区没有数据,线程会立即被挂起,让出CPU资源。这种机制有三大特征:
- 线程资源独占:每个连接需要独立的线程处理
- 编程模型简单:代码顺序执行,符合人类直觉
- 资源利用率低:线程大部分时间在等待IO
我曾经做过一个压力测试:在4核机器上用阻塞IO处理1000个并发连接,结果CPU利用率始终上不去,因为大量线程在等待IO时处于休眠状态。
2.2 非阻塞IO的突破
cpp复制// 设置非阻塞模式
fcntl(fd, F_SETFL, O_NONBLOCK);
// 非阻塞读取
int n = read(fd, buf, sizeof(buf));
if (n < 0 && errno == EAGAIN) {
// 数据未就绪,立即返回
}
非阻塞IO通过fcntl设置O_NONBLOCK标志实现。它的核心改进在于:
- 立即返回机制:无论数据是否就绪,调用立即返回
- 主动轮询检查:需要程序自己不断
