由 if (false) 保护的数据竞争...标准是怎么说的?

JMC*_*JMC 18 c++ concurrency memory-model language-lawyer

考虑以下情况

// Global
int x = 0; // not atomic

// Thread 1
x = 1;

// Thread 2
if (false)
    x = 2;
Run Code Online (Sandbox Code Playgroud)

根据标准,这是否构成数据竞争?[intro.races] 说:

如果两个表达式求值之一修改内存位置 (4.4),而另一个表达式求值读取或修改同一内存位置,则两个表达式求值会发生冲突。

如果程序的执行包含两个潜在并发冲突的操作,并且至少其中一个操作不是原子操作,并且两者都发生在另一个操作之前,则该程序的执行将包含数据争用,除了下面描述的信号处理程序的特殊情况之外。任何此类数据竞争都会导致未定义的行为。

从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值” x = 2;

从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?

激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:

// Thread 1:
r1 = y.load(std::memory_order_relaxed);
if (r1 == 42) x.store(r1, std::memory_order_relaxed);
// Thread 2:
r2 = x.load(std::memory_order_relaxed);
if (r2 == 42) y.store(42, std::memory_order_relaxed);
// This is allowed to result in r1==r2==42 in c++11
Run Code Online (Sandbox Code Playgroud)

(比较https://en.cppreference.com/w/cpp/atomic/memory_order

MSa*_*ers 20

关键术语是“表达评估”。举个非常简单的例子:

int a = 0;
for (int i = 0; i != 10; ++i) 
   ++a;
Run Code Online (Sandbox Code Playgroud)

有 1 个表达式++a,但有 10 个评估。这些都是有序的:第五次评估发生在第六次评估之前。并且 的评估++a与 的评估是交错的i!=10

所以,在

int a = 0;
for (int i = 0; i != 0; ++i) 
   ++a;
Run Code Online (Sandbox Code Playgroud)

有 0 条评价。通过一个简单的重写,我们得到了

int a = 0;
if (false)
   ++a;
Run Code Online (Sandbox Code Playgroud)

现在,如果有 10 个评估++a,我们需要担心所有 10 个评估是否与另一个线程竞争(在更复杂的情况下,答案可能会有所不同 - 比如说,如果您在 时启动一个线程a==5)。但如果根本没有评估++a,那么显然就没有赛车评估。


use*_*522 6

根据标准,这是否构成数据竞争?

不,数据竞争与对表达式中存储位置的访问有关,这些表达式实际上是按照您的报价状态进行评估的。在if (false) x = 2;表达式中x = 2;从不求值。因此,确定数据争用的存在根本不重要。

从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值”x = 2;?

是的。

从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?

如果这样做会影响程序的可观察行为,则不允许这样做。否则它可能会这样做,但不可能观察到差异。

激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:

那是完全不同的情况。该程序也没有任何数据竞争,因为在两个线程中访问的唯一变量是原子变量,永远不会有数据竞争。它仅仅具有潜在的多个有效结果,这意味着竞争条件。数据竞争总是意味着未定义的行为,而不仅仅是未指定的行为

此外,无中生有的问题只是由于多个原子之间的访问的循环依赖而出现的。在您的第一个示例中,只有一个变量,非原子变量,并且没有任何此类循环依赖性。