Skip to content
团子云技术 Lite 1.048596
Go back

高性能线程模型梳理

从数据传输和网络框架的角度,常见高性能线程模型可以按”谁处理数据、谁响应事件、谁执行计算”的职责划分来归类。


1. Run-to-Completion(RTC)

一句话:一个线程拿到请求后从头处理到尾,不切换上下文。

epoll_wait → accept → read → 业务逻辑 → write → epoll_wait
       │                                              │
       └──────── 全在一个线程内搞定 ──────────────────┘
优点缺点
零上下文切换,cache 热单个请求阻塞会饿死后续请求
无锁,无共享状态多核利用率差,需要多进程/多实例
代码简单线性不能有 sleep / 慢 I/O

代表:Seastar、DPDK、早期的 Redis(单线程)、Nginx worker(每个 worker 独立 RTC)。


2. Reactor / Proactor(事件驱动)

一句话:一个事件循环线程负责监听 IO,把就绪的 fd 分发给 handler。

Reactor(同步 IO)

main loop:                            handler:
  epoll_wait ──→ fd 就绪                  │
       │                            read() / 业务逻辑
       ├──→ dispatch(handler)      (handler 自己读)

Proactor(异步 IO)

main loop:                            handler:
  等待 OS 完成 IO ──→ 数据已在 buffer     │
       │                            直接用,无需再 read
       └──→ dispatch(handler)
模式谁读数据典型实现
Reactor应用自己 readlibevent、libuv、Netty
ProactorOS 帮你读了IOCP(Windows)、io_uring(Linux)

常见变体:单 reactor + 线程池(Netty 主从 reactor、memcached)。


3. Half-Sync / Half-Async

一句话:IO 层异步(reactor),业务逻辑层同步(线程池),中间用队列连接。

[异步层]                     [队列]              [同步层]
reactor thread                                   worker pool
     │                                              │
  epoll → 事件入队 ────────────→ 队列 ──→ worker 取出 → 业务逻辑

                                         返回结果入队 → reactor 写出
优点缺点
IO 和计算解耦队列是竞争点
业务逻辑写同步代码即可多次上下文切换
worker 池可伸缩延迟增加

代表:Thrift、gRPC 的默认线程模型、早期的 muduo。


4. Leader-Follower

一句话:一组线程轮流当 leader 负责 accept/监听事件,有请求后变成 follower 去处理,另一个线程接班当 leader。

[Thread 1: Leader] ─→ epoll_wait ─→ 拿到请求 ─→ 变成 follower 处理

[Thread 2] ─→ 发现 leader 走了 ─→ 自己变成 leader ─→ epoll_wait
优点缺点
无中心调度器,线程自治所有线程共享一个 epoll fd 有惊群
请求直接由处理线程接收实现复杂(状态机)

代表:ACE(自适应通信环境)、部分 Java Reactor 框架。


5. Staged Event-Driven Architecture(SEDA)

一句话:把处理拆成多个 stage,每个 stage 有自己独立的线程池和队列,阶段间靠队列传递。

[accept stage] → 队列 → [decode stage] → 队列 → [dispatch stage] → 队列 → [reply stage]
   线程池 A              线程池 B              线程池 C              线程池 D

每个 stage 可以独立调节线程数、队列长度、拒绝策略,天然支持背压。

优点缺点
每阶段独立调优多次跨线程+跨队列,延迟累加
天然管道化,可观察性好队列深度设计不当会雪崩
背压内建代码被队列割裂

代表:SEDA(Berkeley 论文)、Netty 的 Pipeline、Kafka broker 内部类似设计。


6. Work Stealing / Fork-Join

一句话:每个线程有自己的队列,干完了就去偷别人的活。

Thread A: [task | task | task] ────────────────→
                                                    Thread C: []
Thread B: [task | task] ───→ 干完了 ──→ steal ──→ [task]
优点缺点
自动负载均衡steal 有开销(需要同步)
无中心调度长尾延迟不可控
递归分解任务天然适配不适合 IO 密集型

代表:Java ForkJoinPool、Go scheduler(work stealing + GMP)、Tokio(Rust)、Intel TBB、Rayon。


7. M:N 协程 / 用户态调度

一句话:M 个协程跑在 N 个 OS 线程上,协程在用户态切换,阻塞不占线程。

Goroutine × 10000 ──→ scheduler ──→ OS Thread × 4

                     runtime 负责:
                     - work stealing
                     - 网络阻塞时自动 yield
                     - 抢占式调度(Go 1.14+)
平台协程实体调度方式
Gogoroutine协作+有限抢占,runtime 调度
Rustasync taskasync/await + 编译器生成状态机 + 用户选择运行时
C++coroutineC++20,无栈协程
ErlangprocessVM 级调度,抢占式,不共享内存

8. Submit-Then-Poll(你的场景)

一句话:异步提交请求到一个独立的数据面引擎,上层自己控制何时检查完成。

app thread:  submit ──→ [队列/硬件] ──→ engine workers
                  ←── poll status ←──  atomic / CQ
和其他模型的关键区别原因
引擎有自己的线程池封装硬件细节(RDMA/CUDA/DPDK)
上层不自带 event loop调度权留给应用框架
无回调避免控制反转,方便批量化
无阻塞overlap 空间最大

代表:Mooncake TE、libibverbs 原生接口、CUDA stream + cudaLaunchHostFunc、NVMe 的 SPDK。


对比总览

模型IO 线程计算线程同步原语适合场景
RTC自处理自处理纯内存/非阻塞 IO
Reactor专用线程专用线程队列通用网络服务
Half-Sync/Half-Async专用线程线程池队列同步逻辑的 IO 密集服务
Leader-Follower轮班轮班请求处理均匀
SEDA多级线程池多级线程池多级队列复杂的管道化服务
Work StealingN/A所有线程都是窃取队列计算密集/递归并行
M:N 协程runtime 调度runtime 调度channel并发连接多、IO 等待多
Submit-Then-Poll引擎线程应用线程atomic/队列硬件近端、数据面

现实中的系统通常是混搭。比如 Mooncake 的典型场景是:

三种模型各管一层,通过队列/atomic 连起来。


Share this post on:

Previous Post
Mooncake TE 阅读手记-01-Buffer Segment Slice 三层抽象
Next Post
韬(τ)定律:后摩尔时代的芯片路线图