因为并发 bug 具有不确定性、不可复现性和对时序的敏感性,传统"跑一遍断言结果"的测试方式很难抓住它们。

展开解析

  • 不可复现:竞态、死锁是否出现取决于线程调度的具体交错顺序,同样代码跑一千次可能只失败一次,测试通过不代表没有 bug。
  • 测试本身改变行为:加日志、断点、sleep 会改变时序,bug 一观察就消失(heisenbug)。
  • 状态空间爆炸:n 个线程的交错执行路径数是天文数字,无法穷举;单元测试只能覆盖其中极少几种交错。
  • 环境敏感:CPU 核数、负载、操作系统调度策略不同,本地通过、CI 或生产才翻车。

应对手段

  • 压力测试:高并发、长时间、多核环境反复跑,提高暴露概率。
  • 注入时序扰动:用工具强制不同调度(如 Java 的 jcstress、Go 的 -race 数据竞争检测、TSan)。
  • 模型检验/形式化:对关键并发协议用 TLA+ 等验证。
  • 设计上规避:缩小共享状态、用不可变数据和成熟并发原语,让代码"不需要测竞态"。

追问方向:数据竞争检测的原理、如何写出可测的并发代码(依赖注入时钟/调度器)。