assertion failed: self.threads.len() < self.max()
主线位置:主线 1 里
thread::spawn登记线程的那一步。线程表、向量时钟、调度分支里的线程数组都是MAX_THREADS长的定长数组(线程、线程局部与 lazy_static)。
flowchart LR
sp["thread::spawn"] --> nt["Execution::new_thread"]
nt --> as{"threads.len() < max()"}
as -->|"是:主线程 + 至多 4 个"| ok["登记,放进 queued_spawn"]
as -->|"否"| e["assertion failed: self.threads.len() < self.max()"]
现象
thread 'threads_5' panicked at …/loom-0.7.2/src/rt/thread.rs:215:9:
assertion failed: self.threads.len() < self.max()
消息里没有「线程」两个字以外的线索,不说上限是几,也不说这是第几个线程。
最小复现
本部分重跑了 E12(exp-loom/pitfalls/tests/pit.rs,threads_4 / threads_5):主线程 spawn n 个空线程再逐个 join。
| spawn 几个 | 结果 |
|---|---|
| 4 | 493 个交错,通过 |
| 5 | 第 1 个交错报上面这句 |
和 model 与 Builder 里的 E12 一致;那里还测了 max_threads = 3 时 spawn 3 个同样报这句。
源码出处
rt/thread.rs:214-215 的 new_thread 先断言,max() 是线程表的容量,来自 Builder.max_threads,默认 DEFAULT_MAX_THREADS = 5,等于 MAX_THREADS。这是含主线程的总数。
不能靠调大 max_threads 绕过:向量时钟是 MAX_THREADS 长的定长数组,max_threads = 6 再 spawn 5 个,在给第 6 个线程的时钟加一处 panic「index out of bounds: the len is 5 but the index is 5」(model 与 Builder)。Builder.max_threads 的文档说它「must be less than MAX_THREADS」,而默认值正好等于 MAX_THREADS,以代码为准:5 是可以的,6 不行。0.6.0 之前这个上限是 4(a4341d0,#314)。
怎么改
- 让主线程承担一个角色(生产者、消费者、或者一个参与竞争的线程),而不是只 spawn 和 join。
- 线程多本来就贵:E2 里 4 个子线程各一步,上限 3 已经是 133 022 个交错(写一个好模型)。多数并发 bug 两三个线程就能复现,先缩小模型。
- 被测代码自己起线程池的(例如运行时),在 loom 下把线程数配成 1–2;tokio 的切换层把 CPU 数固定为 2 就是这个目的(cfg(loom) 切换层)。
小结
- 最多 5 个线程,含主线程;第 5 个子线程触发
thread.rs:215的断言,消息不提上限。 - E12:spawn 4 个 493 个交错通过,spawn 5 个第 1 个交错报错。
max_threads只能调小,调到 6 会越界。- 改法:主线程也干活;缩小模型;被测代码的线程池在 loom 下配小。
💬 关联讨论
发起讨论加载中...