pytest默认不等待线程结束,测试函数返回即判定通过,必须显式调用t.join(timeout=3)并捕获子线程异常、避免死锁、用event同步而非sleep,确保线程真正结束且结果可验证。

pytest 默认不等待线程结束
测试函数一返回,pytest 就判定用例通过,不管 threading.Thread 是否还在跑。t.start() 后直接 return,后台线程可能只执行了前几行就中断,或根本没跑完——结果就是断言检查的是中间态甚至未初始化的值,看似通过实则漏掉竞态。
- 必须显式调用
t.join(timeout=3),timeout 不能省略或传None - 多个线程要逐个
join,不能只对最后一个调用 - 超时值建议设为 2–5 秒:太短可能误判,太长拖慢 CI
线程内异常会被静默吞掉
子线程抛出未捕获异常,默认不会传播到主线程,sys.excepthook 不生效,pytest 完全感知不到——你看到绿色 PASSED,其实某个线程已经崩了。
- 必须在 worker 函数里包
try/except,把异常存到共享变量(如errors = []) - 主线程最后检查
errors长度,非空就raise errors[0] - 别依赖
logging或 print:它们不阻塞,也不保证输出可见
锁顺序错乱导致死锁卡住整个测试
pytest 不干预你代码里的 threading.Lock 行为,pytest-xdist 或 concurrentpytest 只负责并行跑测试函数,不帮你理清锁依赖。两个线程交叉抢两把锁,大概率卡死在 lock.acquire() 上,测试进程挂住不动。
- 避免裸
with lock:嵌套;改用lock.acquire(timeout=0.5)并检查返回值 - 统一加锁顺序:比如所有地方先
lock_a再lock_b - 测试中优先用
threading.Event或threading.RLock替代多把普通 Lock
time.sleep() 不是同步手段
靠 time.sleep(0.1) 让线程“错开执行”来制造竞态,既不可靠又难复现——CPU 调度、GIL 切换时机完全不确定,有时睡完刚好错开,有时还是串行。
- 用
threading.Event控制节奏:所有线程ready.wait()齐步,ready.set()同时触发 - 想验证中间状态是否被破坏(如计数器出现负值),光比最终值不够,得在临界区插日志或快照
- 不要在测试里模拟“真实延迟”,那属于集成环境问题,应单独用 mock 或重试机制覆盖
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











