“或承诺一个针对普通访问、同步操作和外部可观察结果的相应 SC 投影。左侧的 DRF 判据与右侧的行为等价范围必须成对给出:有的模型在所有 SC 执行上定义 race free,有的直接约束语…”
形式陈述 ​
数据竞争是相对于特定内存模型定义的执行性质。以 C/C++ 式骨架为例,两个动作
属于不同线程,且在该模型中 potentially concurrent; - 至少一个访问不是模型认可的原子访问;
- 既无
,也无 。
这里的
“内存位置”“potentially concurrent”“原子”与对象生命周期由语言定义。Java、C++ 与硬件 litmus test 使用相近术语,却未必给同一执行集合;本页不把某一语言细节冒充跨模型定理。
在 C/C++ 中,只要程序执行含普通非原子 data race,程序行为通常进入未定义行为范围;编译器可据“良构程序无 race”作优化,结果不只限于某几种交错。Java 对 racy 程序仍施加不同约束,硬件模型又只描述指令执行。后果必须随定义来源一起引用。
Data race 也不同于 race condition。后者泛指正确性依赖事件先后,可以完全由合法原子操作组成;消除 data race 只排除未排序的冲突普通访问,不保证复合逻辑原子、确定性或业务不变量。
直觉
Data race 检查的是一对具体访问:它们是否碰到同一位置、是否冲突、是否至少一方非原子,以及模型能否证明顺序。线程数很多、结果偶尔变化或 CPU 同时运行,都不是定义条件。
锁与 release/acquire 向历史加入 happens-before 边,原子类型则改变访问本身的冲突规则。二者都能让特定访问对不再是 data race,却通过不同机制工作;把字段换成 atomic 不会自动把由多次访问组成的事务变成单一步骤。
例子与边界
从普通整数 x = 0 开始,两个线程都执行 x = x + 1。若两边先读到
若两段更新都在同一互斥锁下,线程 unlock,该解锁 synchronizes-with 线程 lock,而锁又先于
若线程 A 只写 array[0],线程 B 只写 array[1],两个元素在语言对象模型中是不同内存位置,则并发写本身不构成 data race。仍可能出现 false sharing,引发缓存行抖动和性能下降;性能共享单元与语言定义的冲突位置是两个层次,不能因为硬件缓存行相同就把访问改判为数据竞争。
再看全原子的检查—行动逻辑:两个线程各自原子读取 balance >= 100,若为真再原子扣除
推论与应用
动态 race detector 依据一次运行观察到的访问与同步关系报告冲突;未报告只说明这条执行未命中。静态类型、所有权系统与分离逻辑能在各自假设下覆盖所有执行,但还需证明其抽象与目标语言内存模型对应。
无数据竞争程序在某些语言中获得接近顺序一致性的语义,是额外的 SC-for-DRF 定理,其前提、原子操作范围和保证随模型变化。数据竞争定义页只负责刻画冲突;它不把模型特定的定理偷渡成无条件结论,也不把“race-free”夸大为程序结果唯一。
参考资料
- ISO/IEC 14882 working draft,
[intro.races]— Data races and related atomic-operation clauses, accessed 2026. - Hans-J. Boehm and Sarita V. Adve, “Foundations of the C++ Concurrency Memory Model,” PLDI 2008, pp. 68–78。
- Oracle, Java Language Specification, Ch. 17: Threads and Locks, Java SE 25, accessed 2026.