对外 API
rayon-core 的公开函数不多,这一部分把它们逐个拆开。每个函数都是 03 任务与 Latch 里几种零件的组合:选一种 Job 装闭包,推进本地 deque、全局注入队列或每线程的广播队列,再选一种 latch 等它(或者不等)。
flowchart LR
subgraph block["阻塞:等任务做完才返回"]
join["join / join_context"]
scope["scope / scope_fifo / in_place_scope"]
bc["broadcast"]
inst["ThreadPool::install"]
end
subgraph detach["不阻塞:交出去就返回"]
sp["spawn / spawn_fifo"]
sbc["spawn_broadcast"]
end
subgraph coop["协作"]
y["yield_now / yield_local"]
end
block --> sj["StackJob 或 HeapJob + 计数 latch"]
detach --> hj["HeapJob / ArcJob + terminate_count"]
coop --> fw["find_work / take_local_job 执行一个任务"]
| 函数 | 闭包约束 | 任务类型 | 进哪个队列 | 等待 | panic 去哪 | 章 |
|---|---|---|---|---|---|---|
join(a, b) | a、b:FnOnce + Send | B 是 StackJob | 本线程 deque | SpinLatch,边等边干活 | 传回调用方;两边都 panic 时传 A 的 | join |
scope(op) | op: Send;任务 Send + 'scope | HeapJob | inject_or_push | CountLatch | 传回调用方,只传一个 | scope |
in_place_scope(op) | op 可以不是 Send | HeapJob | 同上 | 同上;池外线程用 LockLatch | 同上 | scope |
spawn(f) | FnOnce + Send + 'static | HeapJob | inject_or_push | 不等 | panic_handler,没有就 abort | spawn |
spawn_fifo(f) | 同上 | HeapJob 进 JobFifo | worker 上走本线程 FIFO,否则注入 | 不等 | 同上 | spawn |
broadcast(op) | Fn + Sync | 每线程一个 StackJob | 每线程的广播队列 | CountLatch | 传回调用方,按线程号取第一个 | broadcast |
spawn_broadcast(op) | Fn + Send + Sync + 'static | 一个 ArcJob | 同上 | 不等 | 同 spawn | broadcast |
yield_now() / yield_local() | — | — | — | — | 被执行任务自己的处理方式 | yield |
ThreadPool 上的同名方法,阻塞的那几个(join、scope、scope_fifo)都是 self.install(|| 全局函数(..)):先进这个池,再调全局函数;全局函数总是作用于「调用线程所在的池」,不在任何池里就用全局池。install 本身和三条进池路径见 in_worker:从哪进池。
五章:
- join 与 join_context:主线 1 的全部细节。先推 B 再跑 A;弹回来的正是 B 就内联执行,否则执行它,本地空了就
wait_until;A panic 时也要等 B 做完。实测:join的固定开销(E1)、等待时偷来的任务和持锁的冲突(E7,含一个确定性复现)。 - scope 家族:
ScopeBase的三个字段;scope与in_place_scope在哪个线程跑op;spawn为什么一律inject_or_push;'scope生命周期;panic 只保留第一个(E11)。 - spawn 与 spawn_fifo:
terminate_count让池在任务跑完前不终止;没有panic_handler就 abort(E11);和 tokio 的spawn对照;在 async 代码里调 rayon 的三种写法(E5)。 - broadcast:每线程一个 FIFO 广播队列,worker 先清自己的广播队列再去偷;一次睡眠竞态的修复(#992)。
- yield_now 与 yield_local:在 rayon 任务里等条件时,把线程让给 rayon 而不是让给 OS。
本部分的实验
实验目录都在 exp-rayon/ 下,机器是 M2 Max(8 性能核 + 4 能效核),macOS,rustc 1.99.1,--release。
| # | 目录 | 看什么 | 章 |
|---|---|---|---|
| E1 | join-granularity/ | fib(36) 扫串行阈值:join 的固定开销和加速比 | join |
| E7 | lock-reentry/ | 持锁调 rayon:等待时偷来的任务在同一线程上再次加锁;确定性复现 + 概率性死锁 | join |
| E11 | panics/ | join / scope / broadcast 传播哪个 panic;spawn / spawn_broadcast 无 handler 时 abort | join、scope、spawn、broadcast |
| E5 | tokio-rayon/ | async 任务里直接调 par_iter、spawn_blocking、rayon::spawn + oneshot 对 tokio 心跳的影响 | spawn |
| E15 | broadcast-busy/ | 一个 worker 忙 200 ms 时 broadcast 要等多久;反向:忙的任务定期 yield_local | broadcast |
| E16 | yield-poll/ | 1 线程池里一个任务自旋等另一个任务:spin_loop 卡死,yield_now / yield_local 一次解开 | yield |
计时类数字(E1、E5、E15)在 1 分钟和 5 分钟负载都低于 3 时取得,各页写了具体负载;E7、E11、E16 是正确性实验,结论不依赖负载,E7 的死锁概率随负载有变化,页面列出了每组的负载。
💬 关联讨论
发起讨论加载中...