Skip to content

数据竞争

Data race · 数据竞态

不同线程对同一内存位置的冲突访问既未按模型同步排序、也未通过原子操作协调的情形。

条目类型
定义

形式陈述

数据竞争是相对于特定内存模型定义的执行性质。以 C/C++ 式骨架为例,两个动作 a,b 冲突,若它们访问同一内存位置,且至少一个是写入或开始/结束该位置对象的生命周期。它们构成 data race,若进一步满足:

  1. a,b 属于不同线程,且在该模型中 potentially concurrent;
  2. 至少一个访问不是模型认可的原子访问;
  3. 既无 a<hbb,也无 b<hba

这里的 <hb 是程序顺序与 synchronizes-with 等边生成的Happens-before 关系。墙上时间先发生不等于 happens-before:两个线程在某次调度中碰巧串行执行,若没有模型承认的同步边,冲突动作仍可能被判为 race。反过来,锁保护下的动作在物理时间上相隔多久都不重要;前一次解锁 synchronizes-with 后一次加锁,传递闭包会排列临界区内访问。

“内存位置”“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。若两边先读到 0,再各写 1,冲突读写之间没有 happens-before,构成 data race。x=1 只是源码级丢失更新的一种图像;在 C/C++ 中不能据此断言结果只可能是 12,因为程序已越过语言定义行为的边界。

若两段更新都在同一互斥锁下,线程 P 的普通写先于 unlock,该解锁 synchronizes-with 线程 Q 随后的成功 lock,而锁又先于 Q 的访问。传递性给出 P 的访问 happens-before Q 的访问,因此不存在这对 data race。证明依赖的是锁语义生成边,不是临界区“看起来不会同时运行”。

若线程 A 只写 array[0],线程 B 只写 array[1],两个元素在语言对象模型中是不同内存位置,则并发写本身不构成 data race。仍可能出现 false sharing,引发缓存行抖动和性能下降;性能共享单元与语言定义的冲突位置是两个层次,不能因为硬件缓存行相同就把访问改判为数据竞争。

再看全原子的检查—行动逻辑:两个线程各自原子读取 balance >= 100,若为真再原子扣除 100。每个单次访问都合法原子,因此没有普通对象 data race;两者却可能同时通过检查,使余额越过业务下界。这是 race condition:复合操作缺少整体线性化。CAS 循环、锁或事务要解决的是这一更高层问题。

推论与应用

动态 race detector 依据一次运行观察到的访问与同步关系报告冲突;未报告只说明这条执行未命中。静态类型、所有权系统与分离逻辑能在各自假设下覆盖所有执行,但还需证明其抽象与目标语言内存模型对应。

无数据竞争程序在某些语言中获得接近顺序一致性的语义,是额外的 SC-for-DRF 定理,其前提、原子操作范围和保证随模型变化。数据竞争定义页只负责刻画冲突;它不把模型特定的定理偷渡成无条件结论,也不把“race-free”夸大为程序结果唯一。

参考资料
关系图谱6 个相邻概念 · 1 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组