调度器
上一部分讲到,任务对调度器只暴露一个接口:拿到 Notified 就调 run()。这一部分回答剩下的问题:Notified 放进哪个队列,哪个线程在什么时候把它取出来。
源码在 runtime/scheduler/。两种调度器各有一个目录,current_thread/ 只有一个 900 多行的文件,multi_thread/ 有 3000 多行,是本卷篇幅最大的部分。
- current_thread:单线程调度器,作为热身。一个
VecDeque加一个注入队列,block_on的循环。 - multi_thread 总览:
Worker、Core、Shared各管什么;Box<Core>为什么能在线程之间交接。 - 本地队列:容量 256 的环形缓冲,一个生产者、多个窃取者,全靠两次 CAS。
- 全局注入队列:带锁的侵入式链表;worker 多久看它一次,这个间隔怎么按任务耗时自动调节。
- worker 主循环:
Context::run、run_task和 LIFO 槽;每 61 次 tick 做一次的维护。 - 任务窃取与空闲:同时找活的 worker 不超过一半;叫醒谁、什么时候睡。
- 协作式调度预算:每次 poll 128 的预算,用完后 tokio 自己的 future 返回
Pending。 - 阻塞线程池:
spawn_blocking的线程池;block_in_place怎么把Core交给新线程。
一个 Notified 在多线程调度器里可能经过的地方:
flowchart TB
spawn[spawn 或 wake] --> where{当前线程是本运行时的 worker,且持有 Core?}
where -->|是| lifo[LIFO 槽]
lifo -->|被新任务挤出| local[本地队列,256 个槽]
local -->|满了,一半溢出| inject[全局注入队列]
where -->|否| inject
local -->|空闲 worker 偷走一半| other[别的 worker 的本地队列]
inject -->|按间隔或本地空了时取一批| local
lifo --> run[run_task:Harness::poll]
local --> run
inject --> run
读这一部分时,记住任务那一部分的一个结论:Notified 只有一个。NOTIFIED 位保证同一个任务同一时刻最多出现在一个队列里,所以上面这些队列之间挪来挪去,挪的永远是唯一的那个引用。
💬 关联讨论
发起讨论加载中...