常见坑
每一页对着一个报错或一个现象:先给最小复现和实测输出,再到源码里找它从哪来,最后给出改法。机制在前面各章讲过的只链过去;能复用前面实验的直接引用数字。本部分新做的复现在 exp-parking_lot/pitfalls/(同线程重入、毒化,共 7 个用例),其余在前面各章的实验目录里。本机 M2 Max(8 性能核 + 4 能效核),macOS,rustc 1.99.1,parking_lot 0.12.5,一律不开 deadlock_detection(第 9 条除外)。
卡住,没有任何报错
- 同一线程第二次 read() 卡住,只在有写者排队时:写者占了
WRITER_BIT,新读者一律排队(E5:parking_lot 与本机 std 都 3/3 卡死)。 - 同一线程第二次 lock() 永远不返回,也没有报错:锁不记录持有者(本部分新测:
Mutex、RwLock写后读、本机 std 都 3/3 卡死)。 - 两个线程都用 upgradable_read,结果一个等另一个;同一线程再拿就卡死:可升级读同一时刻只有一个。
- 开了 deadlock_detection,程序卡死了却什么也没报:要自己轮询;带超时的等待、std 锁、
Condvar都是盲区(E8)。
编译错误或 panic
- future cannot be sent between threads safely:*mut () 不是 Send:守卫默认
!Send;开send_guard能编译但current_thread运行时 3/3 卡死(E9)。 - the
send_guardanddeadlock_detectionfeatures cannot be used together:两个依赖各开一个,Cargo 合并后撞上。 - Condvar::wait 报 E0308:expected MutexGuard, found MappedMutexGuard:映射守卫、
FairMutex的守卫都不能传给wait。 - attempted to use a condition variable with more than one mutex:第二个
wait调用时立即 panic(E15)。 - Once instance has previously been poisoned:初始化 panic 后毒化,等待者全部跟着 panic(E10)。
数据或时间和预期不同
- 持锁时 panic,下一个线程照常拿到锁和改了一半的数据:没有毒化(本部分新测:
a + b从 100 变成 90,std 返回PoisonError)。 - 一个线程放锁后立刻又拿到,另一个线程等了几千次:允许插队;0.12.5 约 1 ms 兜底,0.13.0 与本机 std 没有(E3)。
- notify_all() 返回 32,可是一个线程也没醒:返回值包括被挪到 mutex 队列的线程(E6)。
- 在 macOS 上把系统时间往回拨,try_lock_for 多等了一大截:Apple 平台的超时按墙上时钟换算(只读代码)。
flowchart TB noowner["锁不记录持有者"] --> p2["2 同线程重入"] wpref["写者一到就挡新读者"] --> p1["1 写者排队时重复读"] onlyone["可升级读只有一个"] --> p3["3 可升级读串行与自锁"] marker["守卫的 Send 由 GuardMarker 决定"] --> p5["5 跨 await"] marker --> p6["6 send_guard 与死锁检测"] dd["死锁检测按线程记账、只看停车者"] --> p4["4 检测盲区"] dd --> p6 cvraw["Condvar 直接改 RawMutex"] --> p7["7 映射守卫与 FairMutex"] cvraw --> p8["8 两把 mutex"] cvraw --> p12["12 notify 返回值"] nopoison["只有 Once 毒化"] --> p9["9 Once 毒化"] nopoison --> p10["10 Mutex 无毒化"] barging["允许插队"] --> p11["11 饿死"] platform["Apple 上 pthread 条件变量"] --> p13["13 墙上时钟"]
按根源分:第 1、2、3 条都是「同一线程既持有又在等」,锁本身发现不了,只有第 2 条能被死锁检测抓到(第 4 条讲它的边界)。第 5、6 条是同一个类型标记在编译期和构建期的两种后果。第 7、8、12 条来自 Condvar 与 parking_lot 自己的 RawMutex 绑死(requeue 要直接改它的状态字节),这也是 08 设计取舍 里 requeue 那一条的代价。第 9、10 条是同一个问题的两面:parking_lot 只有 Once 毒化。按后果分:第 1–4 条卡住,第 1、3 条依赖时序或调用路径,测试里不一定出现;第 5–9 条在编译期或第一次出错时就暴露;第 10–13 条结果不对或时间不对,最难发现。
规划时没有实测的三条
卷计划把候选里的最后三条(可升级读、Once 毒化、映射守卫配 condvar)标为「先测,不成立或太小众就删」:
- 可升级读:第 2 批的
exp-parking_lot/pitfalls-upgradable测出两个可升级读者互相排队(B 等了约 206–210 ms)、三种同线程写法 3/3 卡死,保留(本页第 3 条)。 Once毒化:第 2 批的 E10 拿到了报错原文,并测出等待中的 4 个线程全部跟着 panic,保留(第 9 条)。- 映射守卫配 condvar:编译错误原文已拿到,并且测出
FairMutex的守卫同样不行,比计划多一种情况,保留(第 7 条)。
三条都成立,没有删掉的候选。
本站对 parking_lot 只有间接依赖;这些坑在本站的依赖里碰不碰得到,见 10 在本站中。
💬 关联讨论
发起讨论加载中...