// TOKIO_RT src/runtime/ RUNTIME ACTIVE
SOURCE-LEVEL DEEP DIVE tokio / src / runtime / v1.x trunk

Tokio Runtime
源码深度剖析
调度内核全解

完全基于 Tokio 官方主干源码,从工程底层拆解异步运行时的调度架构、线程模型、三级任务队列、Work-Stealing 无锁窃取、任务预算机制、Waker 唤醒链路、IO 驱动联动、阻塞隔离池全套内核实现。

4
CORE MODULES
3
QUEUE TIERS
256
LOCAL Q SIZE
O(1)
TIMER TICK
01
[ 01 / 11 ]

整体目录架构与内核分层

调度层、任务层、驱动层、阻塞隔离层四大正交模块,依赖单向、无循环耦合。

Tokio Runtime 所有调度能力收敛在 src/runtime,整体分为调度层、任务层、驱动层、阻塞隔离层四大正交模块,模块完全解耦、依赖单向、无循环耦合,是高可靠底层库的标准架构范式。

tokio/src/runtime/ — directory tree
// Tokio 异步运行时源码目录结构
src/runtime/
├── multi_thread/     // 默认多线程工作窃取调度器(工业核心)
├── current_thread/   // 单线程嵌入式调度器(客户端/wasm)
├── task/             // 任务元数据、状态机、Waker 实现
├── queue/            // 无锁任务队列、共享/全局队列实现
├── driver/           // IO 反应器 + 分层时间轮定时器
├── blocking/         // 阻塞任务隔离线程池
├── worker.rs         // Worker 线程主循环、任务执行逻辑
├── runtime.rs        // 运行时顶层实例、构建器配置
└── mod.rs            // 模块统一导出与调度入口
// RUNTIME ESSENCE Tokio 运行时核心本质:基于协作式抢占、无锁任务队列、分层调度、硬件亲和线程池的用户态异步执行器。Rust 语言无内置运行时,所有 Future 推进、任务挂起唤醒、IO 事件驱动、线程负载均衡全部由该模块自主实现。
02
[ 02 / 11 ]

两种调度器源码对比

multi_thread vs current_thread —— 工业服务端 vs 嵌入式客户端的取舍。

DEFAULT · MULTI_THREAD

多线程调度器

src/runtime/multi_thread/mod.rs

服务端高并发场景唯一默认调度器。多 Worker 线程、Work-Stealing 负载均衡、三级任务队列、线程安全任务提交、支持大规模任务并行。

  • Worker 线程数默认等于 CPU 核心数
  • 每个线程独立拥有本地无锁队列
  • 全局共享中继队列与兜底全局队列
  • 最大化无锁执行路径占比
  • 最小化跨线程竞争开销
EMBEDDED · CURRENT_THREAD

单线程调度器

src/runtime/current_thread/mod.rs

无线程池、无窃取逻辑、仅单线程循环执行任务。队列采用普通 VecDeque,无需原子操作与无锁逻辑,适合 WASM、客户端、轻量工具场景。

  • 无并行能力,单线程串行执行
  • 普通 VecDeque 队列,零原子开销
  • 无法隔离阻塞任务
  • 长时间任务会卡死整个运行时
  • 适用于资源受限环境
03
[ 03 / 11 ]

任务内核 · 状态机与内存模型

Task 是 Tokio 最小调度单元,Arc 引用计数 + 原子状态机管理生命周期。

Task 是 Tokio 最小调度单元,源码完全采用 Arc 引用计数 + 原子状态机 管理生命周期,无 GC、无内存泄漏、无悬垂指针。

3.1 核心数据结构 task.rs

每个 tokio::spawn 生成一个 Task,由两层结构组成:

// HEADER

TaskHeader

原子状态、引用计数、Waker、调度归属、任务优先级

// PAYLOAD

TaskFuture

用户业务 Future 状态机,存储 poll 上下文与中间状态

// HANDLE

JoinHandle

用户持有结果等待句柄,与 Task 共享 Arc 内存

// MEMORY RULE — HARDCODED 内存回收铁律:只有调度器丢弃任务引用 + 用户丢弃 JoinHandle,任务内存才会释放,彻底杜绝 poll 过程中内存提前释放的 UB。

3.2 任务四状态原子流转

源码定义四态原子枚举,所有状态切换为无锁原子 CAS 操作:

Ready
任务可执行
等待队列调度
Running
正在被 Worker
线程 poll 执行
Parked
Poll::Pending 挂起
注册 Waker 等待资源
Completed
Future 执行完毕
缓存返回结果
schedule()
poll()
Pending
Ready

整个状态流转无锁、无阻塞、无系统调用,是 Tokio 超高吞吐的底层根基。

3.3 官方静态 Waker 实现 waker.rs

Tokio 不使用标准库默认 Waker 动态虚表,而是全局静态只读虚表,彻底消除堆分配开销。源码固定四大虚函数:clone、wake、wake_by_ref、drop。

waker.rs — RawWaker vtable (simplified)
static VTABLE: RawWakerVTable = RawWakerVTable::new(
    clone,         // 引用计数 +1,零堆分配
    wake,          // 消耗 Arc,状态置 Ready,推入调度队列
    wake_by_ref,   // 不消耗 Arc,仅唤醒
    drop,          // 引用计数 -1
);

// wake 核心逻辑
fn wake(ptr: *const ()) {
    let task = unsafe { Arc::from_raw(ptr as *const Task) };
    task.state.transitions_to_ready();   // CAS 无锁切换
    task.scheduler.push(task);               // 推入对应调度队列
    worker.unpark();                        // 唤醒休眠 Worker
}
04
[ 04 / 11 ]

调度核心 · 三级任务队列

本地队列 → 共享中继队列 → 全局队列,95% 任务全程无锁执行。

为解决高并发锁竞争,Tokio 设计本地队列 → 共享中继队列 → 全局队列三级分层队列,95% 任务全程无锁执行。

L1 · LocalQueue
Chase-Lev 无锁双端队列
固定容量 256,基于 UnsafeCell + 原子头尾索引实现。所有者 Worker 仅操作尾部 push/pop,零锁零 CAS 冲突;其他 Worker 仅从头部 steal,原子 CAS 抢占。
LOCK-FREE
~95%
HIT RATE
L2 · SharedQueue
共享中继缓冲层
本地队列与全局队列的缓冲层,采用分段锁设计,打散锁竞争域,避免瞬时大批量 spawn 触发全局锁争抢,大幅削峰高并发压力。
SHARD-LOCK
~4%
SPILL
L3 · GlobalQueue
全局兜底队列
唯一带全局互斥锁的兜底队列,访问频率极低,仅在本地、共享队列全部溢出时使用,不会成为性能瓶颈。
MUTEX
~1%
FALLBACK
// DESIGN INSIGHT 该隔离设计让绝大多数任务在 CPU 缓存亲和的本地线程执行,无跨核同步开销。本地队列满时,任务溢出至共享队列。
05
[ 05 / 11 ]

Worker 线程主循环流程

所有业务异步代码最终进入 worker.rs run() 循环调度。

worker.rs 的 run() 是整个运行时的执行入口,所有业务异步代码最终进入该循环调度,官方固定优先级执行顺序:

STEP 01
LIFO 缓存任务优先执行

热点任务复用,提升 CPU 缓存命中率。最近 spawn 的任务最有可能仍驻留在 L1 cache 中。

STEP 02
消费本地无锁队列任务

从当前 Worker 拥有的 Chase-Lev 队列尾部 pop 任务,无任何锁、无 CAS 冲突,是最热路径。

STEP 03
消费共享中继队列任务

当本地队列为空时,从 SharedQueue 批量拉取任务回填本地队列,分段锁降低争抢。

STEP 04
按动态间隔抽取全局队列

防止全局任务饥饿,每隔 61 次本地调度轮询一次全局队列,保证公平性。

STEP 05
跨线程 Work-Stealing 窃取

从其他 Worker 本地队列头部偷取任务,一次偷对方一半,大幅减少窃取次数与 CAS 竞争。

STEP 06
无任务则 park 休眠

释放 CPU,通过 condvar 或 epoll_wait 阻塞等待新任务到达后被唤醒。

// STEAL OPTIMIZATION — 2019 半量窃取是 Tokio 10 倍性能优化的核心改动(官方 2019 调度升级文档要点)。窃取不偷单个任务,而是一次性窃取对方队列一半任务。窃取重试次数受限,避免空转自旋浪费 CPU。

5.2 任务预算机制 Budget

源码内置任务执行预算,单个任务连续 poll 次数受限,防止超长任务霸占线程、导致其他任务饥饿。预算耗尽自动让出线程,保证调度公平性,实现协作式有限抢占。

06
[ 06 / 11 ]

驱动层 · IO 反应器与时间轮

MIO 封装跨平台 IO 多路复用 + 分层时间轮 O(1) 定时器。

// DRIVER · IO

IO 反应器 Driver

基于 MIO 封装跨平台 IO 多路复用(epoll / kqueue / IOCP)。

核心设计:懒注册、批量事件分发、零冗余系统调用。Future 因 IO 阻塞返回 Pending 时,驱动注册 fd 与 Waker;IO 就绪后批量唤醒任务,重新入队调度,彻底避免阻塞线程。

// DRIVER · TIMER

分层时间轮 Timer

摒弃线性遍历,采用多层时间轮结构。

定时任务到期检测为 O(1) 复杂度,百万级定时任务无性能衰减。定时器唤醒与任务调度完全复用 Worker 队列体系,架构统一。

driver/ — IO 事件循环骨架
fn turn(&mut self, max_wait: Option<Duration>) {
    // 1. 计算最近定时器到期时间,与 max_wait 取小
    let timeout = self.timer.next_timeout().min(max_wait);
    
    // 2. 一次性系统调用,批量获取就绪事件
    let events = self.reactor.poll(timeout);
    
    // 3. 批量分发:fd → Waker → 任务入队
    for ev in events {
        ev.waker.wake_by_ref();
    }
    
    // 4. 处理已到期定时器
    self.timer.process_expired();
}
07
[ 07 / 11 ]

阻塞隔离池 · blocking/

异步 Worker 线程绝对禁止同步阻塞,独立线程池隔离污染。

Tokio 严格区分异步任务与阻塞任务,异步 Worker 线程绝对禁止同步阻塞。所有文件 IO、同步数据库、sleep 阻塞逻辑必须通过 spawn_blocking 投递至独立线程池。

// 01

动态扩容

按需创建阻塞线程,上限可配,避免无限制线程爆炸

// 02

独立生命周期

阻塞线程不参与 Work-Stealing,完全脱离核心调度

// 03

隔离调度队列

独立队列,不污染 Worker 调度流水线

// 04

架构级杜绝卡死

从架构层面杜绝 Runtime 卡死,保护核心 IO 线程

blocking/ — spawn_blocking 入队示意
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
where F: FnOnce() -> R + Send + 'static
{
    // 1. 任务包装为 Job,推入独立阻塞池队列
    let (task, handle) = Task::blocking(f);
    self.blocking_pool.spawn(task);
    
    // 2. 若空闲线程不足且未达上限,动态创建新线程
    if self.idle_count.load() == 0 {
        self.spawn_thread();
    }
    
    handle
}
08
[ 08 / 11 ]

源码层面核心设计哲学

无锁优先、零成本抽象、缓存亲和、公平调度 —— 四大工程取舍。

// 01 · CONCURRENCY

无锁优先 · 锁做兜底

95% 任务执行路径全程原子无锁,互斥锁仅用于极端兜底场景,最大化并发吞吐、最小化内核上下文切换。

// 02 · ABSTRACTION

零成本抽象严格落地

无协程栈分配、无 GC、无动态调度开销、无虚表动态分配,async/await 完全是编译期语法糖,运行时无额外成本。

// 03 · CACHE

缓存亲和性优先

优先本地队列执行、热点任务 LIFO 复用、批量窃取任务,全方位提升 CPU 缓存命中率,是高性能底层库的关键优化。

// 04 · FAIRNESS

公平调度防饥饿

全局队列定时抽取、任务预算限制、半量窃取机制,兼顾高吞吐与任务公平性,避免长尾任务饥饿。

09
[ 09 / 11 ]

Runtime 完整闭环执行链路

从 spawn 到内存释放,源码级复盘六步闭环。

01
tokio::spawn

将 Future 封装为 Arc Task,根据当前 Worker 状态优先推入本地队列。

02
Worker 主循环

按优先级取出任务,执行 poll 推进状态机,状态由 Ready 转 Running。

03
注册 Waker

任务 Pending 时绑定 Waker 至 IO/定时器驱动,线程让出执行权,状态转 Parked。

04
资源就绪触发 wake

驱动事件就绪,调用 wake,任务重新标记 Ready 并入队。

05
poll 返回 Ready

任务执行结束,状态转 Completed,缓存结果并唤醒 JoinHandle。

06
引用计数归零

Task 内存安全释放,无泄漏,无 UB,无 GC 暂停。

10
[ 10 / 11 ]

工业级源码落地避坑

基于真实内核逻辑的五条铁律,违反即灾难。

!
禁止 Worker 内同步阻塞

会冻结当前线程调度,破坏整个并发流水线,必须使用 spawn_blocking。

!
自定义 Future 必须正确注册 Waker

遗漏注册会导致任务永久挂起,无唤醒机会,是手写 Future 最常见的死锁源。

!
避免瞬时超量 spawn

打满本地队列会触发共享/全局锁竞争,吞吐下降。应做批量节流或背压控制。

!
高频复用 Waker

频繁 clone Waker 会产生大量原子 CAS 开销,高并发场景需缓存复用 waker 引用。

!
区分 IO 密集 / CPU 密集任务

CPU 密集任务容易耗尽预算,导致 IO 任务延迟升高,需配合 spawn_blocking 或 yield_now。

11
[ 11 / 11 ]

总结

通读 tokio/src/runtime 全套源码后的核心结论。

通读 tokio/src/runtime 全套源码可以得出:Tokio 绝非简单的 epoll 封装工具库,而是一套自主实现任务管理、线程调度、负载均衡、事件驱动、内存生命周期、阻塞隔离的完整工业级操作系统级并发内核。

其核心竞争力来源于:Chase-Lev 无锁队列的极致并发、分层队列的锁竞争规避、半量窃取的负载均衡、任务预算的公平调度、静态 Waker 的零开销、IO/时间驱动的精准系统调用裁剪。所有设计细节均服务于 Rust 零成本抽象哲学,让 Web、网关、微服务、分布式中间件能够压榨硬件极致并发性能。

CORE KEYWORDS

Work-Stealing Chase-Lev Queue Zero-Cost Abstraction Cooperative Preemption Static Waker VTable Hierarchical Timer Wheel Arc Task CAS State Machine LIFO Slot Budget Half-Steal MIO Reactor spawn_blocking Cache Affinity Fair Scheduling
// REFERENCE · SOURCE PATHS
tokio/src/runtime/multi_thread/worker.rs — Worker 主调度循环
tokio/src/runtime/queue/local.rs — Chase-Lev 无锁队列
tokio/src/runtime/task/ — 任务状态机与 Waker 实现
tokio/src/runtime/driver/ — IO 与定时器驱动
tokio/src/runtime/blocking/ — 阻塞任务隔离线程池