【问题标题】:Data race guarded by if (false)... what does the standard say?由 if (false) 保护的数据竞争……标准是怎么说的?
【发布时间】:2022-06-10 16:40:20
【问题描述】:

考虑以下情况

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

// Thread 1
x = 1;

// Thread 2
if (false)
    x = 2;

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

如果其中一个修改了内存位置 (4.4) 而另一个读取 或修改相同的内存位置。

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

从语言律师的角度来看是否安全,因为永远不允许程序执行“表达式评估”x = 2;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

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

【问题讨论】:

  • 哪个体面的编译器会从 if (0) 发出代码?
  • 无,但问题被标记为“语言律师”。考虑这个问题:如果编译器没有删除 if(0) foo();然后通过投机执行或任何其他转换导致数据竞争在技术上仍符合合同标准?标准是否强制这种行为,或者它是否属于“未定义行为”,给予任何合规的编译器许可来做任何事情?
  • @Fareanor,Re,“无论如何,代码永远不会被执行。”问题不在于任何理智的实现做什么。问题在于language-lawyer 认为标准可能允许 实现做什么。 OP 专门询问了一个实现,它开始执行x=2 分配并同时测试if 条件,然后在发现条件为假时取消或“回滚”操作。
  • @DanielLangr:也高度相关:What formally guarantees that non-atomic variables can't see out-of-thin-air values and create a data race like atomic relaxed theoretically can? - 凭空出现的问题只是mo_relaxed 形式上的一个差距,而不是这适用于普通对象。 (而且任何真正的实现都不会允许原子操作;C++ 委员会打算禁止它。)引入影响行为的数据竞争将违反 as-if 规则。 (另见lwn.net/Articles/793253

标签: c++ concurrency language-lawyer memory-model


【解决方案1】:

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

int a = 0;
for (int i = 0; i != 10; ++i) 
   ++a;

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

所以,在

int a = 0;
for (int i = 0; i != 0; ++i) 
   ++a;

有 0 个评价。通过一个微不足道的重写,这让我们得到了我们

int a = 0;
if (false)
   ++a;

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

【讨论】:

    【解决方案2】:

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

    不,数据竞争与访问表达式中的存储位置有关,正如您的报价所述,这些表达式实际上是 求值 的。在if (false) x = 2; 中,表达式x = 2; 永远不会被计算。因此,确定是否存在数据竞争根本无关紧要。

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

    是的。

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

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

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

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

    此外,凭空出现的问题仅作为多个原子之间访问的循环依赖的结果出现。在您的初始示例中,只有一个变量,非原子且没有任何此类循环依赖。

    【讨论】:

    • "如果它可能影响程序的可观察行为" — 但如果程序格式正确(例如,如果线程是及时分开)。所以编译器实际上可以进行这种推测执行。
    • @Ruslan 如果线程同时执行,该程序也是格式良好的,在这种情况下,如果可以在机器级别上编译器可能不会引入这样的推测存储,例如导致撕裂,最终值 x 不同于 1 是可观察的。当然,我回答的那一段实际上毫无意义,因为它只是重申了一般的 as-if 规则,即编译器可以以任何方式翻译程序,从而在抽象机的规则下保留可观察的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多