写入相同值的竞争条件是否安全?

Are race conditions that write the same value safe?

假设我有一些使用共享内存的多线程程序,其中多个线程随机覆盖一些多字节变量的值(例如intdouble),有时会相互碰撞(a.k.a。竞争条件),并且也会随机读取同一变量的值。

假设所有线程总是将相同的值写入内存地址(例如,每个线程都会x = 1000)——如果一个线程在确切的时刻读取变量另一个线程 is/are 覆盖它,变量是否保证具有正确的值? 或者内存是否会以某种方式被随机覆盖?

也就是说,如果所有线程总是写入x = 1000,一个线程是否可以读取x并得到除1000之外的东西?

Assuming all the threads always write the same value to the memory address (e.g. each thread does x = 1000) - if a thread reads the variable at the exact moment that another thread(s) is/are overwriting it, is the variable guaranteed to have the correct value?

C 语言规范通过声明包含竞争条件的程序的行为是未定义的,明确拒绝做出这样的保证。而且你是对的,没有同步,你描述的情况是竞争条件,无论写入的值是否与内存的初始内容相同。

or could the memory somehow get overwritten with something random?

行为未定义。原则上,任何事情都可能发生,包括读取看到一个从未存储在相关位置的值。

另请注意,比赛与任何一种 objective 同时性无关。相反,它是关于缺乏同步会阻止同时访问,无论是否实际发生任何同时访问。

在实践中,您可能会发现,在某些实现中,至少在某些情况下,不更改内存内容的写入行为就像它们彼此不冲突或与发生在该值之后的读​​取一样首次写入位置,其中“发生在之后”是一个部分取决于同步的技术术语。但是,我不建议依赖于这种行为。即使有记录也不行。

C 标准允许以低廉的成本提供比它要求的更强大的保证的实现。因此,该标准向后弯曲以避免要求实施维护其成本有时可能超过其收益的任何保证,留下了如何以及何时维护其收益超过成本的保证的问题作为标准管辖范围之外的实施质量。

虽然看起来似乎不需要花费任何成本来保证写入一个对象已经包含的值不会产生任何影响,但坚持这样的保证有时需要放弃一些可能有用的优化。作为一个简单的例子,考虑下面的函数

volatile zz;
unsigned test(unsigned *p, unsigned *q)
{
  unsigned temp;
  *p = 0x1234;
  temp = *q;
  zz = 1;
  do {} while(zz);
  *p = 0x1235;
  return temp;
}

在某些平台上,包括原始的 8088/8086,处理代码的最有效方法可能是将最后一次分配给 *p 替换为 *p += 1;,然后可以使用inc 指令。但是,如果代码同时在两个线程中执行,则可能导致 *p 保留值 0x1236。

在许多情况下,坚持保证用它已经包含的值写入一个对象不会花费任何成本,而将涉及此类写入的竞争条件视为良性将消除一些不必要的同步操作的成本。不幸的是,虽然标准允许实现提供超出标准要求的保证,但这样做是实用和有用的,它没有提供区分提供此类保证的实现与不提供此类保证的实现的方法。