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)
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,那么显然就没有赛车评估。
根据标准,这是否构成数据竞争?
不,数据竞争与对表达式中存储位置的访问有关,这些表达式实际上是按照您的报价状态进行评估的。在if (false) x = 2;表达式中x = 2;从不求值。因此,确定数据争用的存在根本不重要。
从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值”x = 2;?
是的。
从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?
如果这样做会影响程序的可观察行为,则不允许这样做。否则它可能会这样做,但不可能观察到差异。
激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:
那是完全不同的情况。该程序也没有任何数据竞争,因为在两个线程中访问的唯一变量是原子变量,永远不会有数据竞争。它仅仅具有潜在的多个有效结果,这意味着竞争条件。数据竞争总是意味着未定义的行为,而不仅仅是未指定的行为。
此外,无中生有的问题只是由于多个原子之间的访问的循环依赖而出现的。在您的第一个示例中,只有一个变量,非原子变量,并且没有任何此类循环依赖性。
| 归档时间: |
|
| 查看次数: |
1275 次 |
| 最近记录: |