报告是地图,不是噪音
竞态报告显示对同一内存的两次访问之间没有 happens-before 关系。一个栈是当前读或写,另一个是此前冲突的访问。两个都要读。如果一侧持锁而另一侧没有,说明锁并没有保护那条路径。
WARNING: DATA RACE
Read at 0x00c000... by goroutine 7:
main.(*Counter).Value()
Previous write at 0x00c000... by goroutine 6:
main.(*Counter).Add()
选择同步原语
互斥锁保护临界区;channel 转移所有权,让共享关系显式;原子操作适合简单计数器和标志。不要为了“更快”把互斥锁和非同步读混用。对 map,要么每次访问都用同一把锁,要么使用并发安全结构。对循环变量,把值传入 goroutine,或为每次迭代创建变量。
复现并保持修复
对失败的测试运行 go test -race -count=100。竞态可能很罕见,重复和高并发能提高检出概率。在 CI 中保留启用竞态的测试套件,至少覆盖有共享状态的包。如果无法复现,记录可疑路径并添加压力测试,而不是关闭问题。
修复与验证
用正确的同步原语保护每个冲突访问,然后在高并发下重复运行竞态检测器和压力测试。确认报告消失、行为不变、吞吐没有因锁粒度过粗而明显下降。把复现用例保留为回归测试;竞态修复后再次出现,通常意味着还有另一条未保护的路径。