两种模式的定义
Reactor 模式: 不主动等待 I/O,而是注册事件处理器,让操作系统在 I/O 就绪时通知。
多线程模式: 不单线程顺序执行,而是创建多个线程并行执行,让多核 CPU 同时处理多个任务。
Reactor 适合 I/O 密集型任务
假设处理 100 个连接,每个连接需要:
- I/O 等待: 90ms(网络传输)
- CPU 计算: 10ms(业务处理)
时间线
0ms: 连接1开始I/O等待(epoll_wait 阻塞)
90ms: 连接1数据到达,处理10ms
100ms: 连接1完成,继续等待其他连接
(此时连接2、3...的数据可能已经到达)
110ms: 处理连接2(10ms)
120ms: 处理连接3(10ms)
...
总时间: 约 100ms(所有连接几乎并行处理)
关键优势
关键在于 I/O 等待期间不占用 CPU,CPU 可以一直处理就绪的事件。单线程也没有线程切换开销,内存占用也小(每个连接一个结构体)。
多线程不适合处理 I/O 密集型任务
如果是每个连接一个线程,那线程的大部分时间都在等待,而且还会给线程调度器带来很大负担,上下文切换也会消耗 CPU 资源。最后,就算线程阻塞,系统也还是要维护线程状态的,这也是资源占用。
因为每个连接都是一个线程,内存占用也会更多。
也就是说,如果是 I/O 密集型任务使用多线程模式,虽然大部分线程都在等待,却仍然会消耗系统资源。
多线程适合处理 CPU 密集型任务
假设处理 100 个任务,每个任务需要:
- CPU 计算: 90ms(复杂计算)
- I/O 等待: 10ms(读取配置)
时间线
0ms: 线程1-4开始计算(4核并行)
90ms: 线程1-4完成计算,开始I/O
100ms: 线程1-4完成
线程5-8开始计算(复用CPU核心)
...
总时间: 约 2500ms(100个任务 ÷ 4核 × 100ms)
总计算能力: 90% × 4核 = 360%(相当于单核的3.6倍)
可见,如果是 CPU 密集型任务,多线程模式能够充分利用每一个 CPU 核心。
Reactor 不适合处理 CPU 密集型任务
采用复杂计算的任务时间假设,时间线是这样的:
0ms: 任务1开始计算(90ms)
90ms: 任务1计算完成,I/O等待(10ms)
100ms: 任务1完成
任务2开始计算(90ms)
...
总时间: 10000ms(100个任务 × 100ms)
CPU 利用率: 90%
多核心并行的优势完全没有发挥,所有的计算任务都堆积到了一个核心上。
总结
Reactor 适合 I/O 密集型任务,因为 I/O 等待期间 CPU 可以处理其他任务,单线程即可高效处理大量并发连接。
多线程适合 CPU 密集型任务,因为 CPU 计算必须顺序执行,无法并行等待(与 I/O 等待不同,I/O 等待的是外部资源,不消耗 CPU,因此多个 I/O 操作可以并行等待),多线程可以充分利用多核 CPU 的并行能力。
这也是为什么 KV 存储特别适合使用 Reactor 的原因:因为在 KV 存储的业务中,一次操作的计算耗时可能不足 0.1ms,绝大多数时间都在等待 I/O。