memory_order_relaxed 是否尊重同一线程内的数据依赖性?

Jos*_*vin 4 c++ multithreading atomic lockless instruction-reordering

鉴于:

std::atomic<uint64_t> x;

uint64_t f()
{
    x.store(20, std::memory_order::memory_order_relaxed);
    x.store(10, std::memory_order::memory_order_relaxed);
    return x.load(std::memory_order::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)

假设只有一个线程写入,是否有可能f返回除 之外的值?对于非原子变量来说显然不是这样,但我不知道relaxed是否如此宽松以至于它会忽略同一线程中的数据依赖关系?10x

Bri*_*ian 6

加载的结果始终是10(假设只有一个线程)。即使是宽松的原子变量也比非原子变量“更强”:

\n
    \n
  1. 与非原子变量一样,所有线程必须就对该变量的所有修改发生的单一顺序达成一致,
  2. \n
  3. 与非原子变量一样,该单一顺序与“之前排序”关系一致,并且
  4. \n
  5. 该实现将保证潜在的并发访问将以某种方式将自己排序为所有线程都同意的某种顺序(从而满足要求 1)。另一方面,在非原子变量的情况下,潜在的并发访问会导致未定义的行为。
  6. \n
\n

宽松的原子变量不能用于使不同的线程彼此同步,除非伴随着显式的栅栏。与适用于原子变量的其他内存顺序相比,这就是它的宽松之处。

\n

对于语言律师,请参阅 C++20 [intro.races]/10:

\n
\n

如果满足以下条件,则评估A 发生在评估B之前(或者等效地,B发生在A之后):

\n
    \n
  • A在B之前排序,或 [...]
  • \n
\n
\n

和[介绍.races]/15:

\n
\n

如果修改原子对象M的操作A发生在修改M操作B之前,则A 的修改顺序应早于B。[注意:此要求称为写入连贯性。\xe2\x80\x94尾注]

\n
\n

和[介绍.races]/18:

\n
\n

如果原子对象M上的副作用X发生在M的值计算B之前,则评估B应从X或按照M的修改顺序从X后面的副作用Y获取其值。[注意:此要求称为写读一致性。\xe2\x80\x94尾注]

\n
\n

因此,在您的程序中,20 的存储发生在 10 的存储之前(因为它在 10 之前排序),并且 10 的存储发生在加载之前。写-写一致性要求保证 10 的存储在修改顺序上x比 20 的存储晚发生。当加载发生时,需要从 10 的存储中取出它的值,因为 10 的存储发生在之前它并且没有其他修改可以遵循 的修改顺序中的 10 存储x

\n