我试图理解在C ++ 11规范中之前发生的含义,特别是规范是否假定除了指定的内容之外还对该术语有任何非正式的理解。我是在N3290草案中工作的。
这个术语应该只针对规范本身进行解释的直接论点是规范实际上是在讨论它自己对术语的定义。例如,1.10.6:"之前(定义如下)"或1.10.11:"'发生在'之前关系,定义如下。"
另一方面,1.10.12读取"评估A发生在评估B之前,如果:......"如果这是之前发生的定义,我希望它能读取"如果,只有。"因此,似乎规范定义了关系之前发生的一些最低要求,但仍然对其他因素持开放态度,其中可能包括该术语含义的一些非正式概念,或者可能包括规范其他部分的语言。
第1.10.13段然后定义了可见的副作用,或者它呢?它看起来像一个定义,因为可见的副作用是斜体,但定义是以前发生的,所以在给出直观的可见副作用概念之前,是否应该将其作为进一步的限制来阅读?换句话说,如果B看到A,这是否意味着A发生在B之前(对之前发生的定义的进一步限制),或者只是意图在另一个方向,即当A发生在B之前,那么A必须是一个明显的副作用。
这是一个具体的例子(受this question的启发,但在这里我只是试图理解之前发生的事情,并且相对于C ++而不是C,因为C ++规范看起来有点清晰):
#include <atomic>
#include <cassert>
#include <thread>
using namespace std;
atomic<int> a;
atomic<int> b;
void
p1()
{
atomic_store_explicit(&a, 1, memory_order_relaxed); // 1
}
void
p2()
{
if (atomic_load_explicit(&a, memory_order_relaxed)) {
//atomic_thread_fence(memory_order_release);
atomic_store_explicit(&b, 1, memory_order_relaxed); // 2
}
}
void
p3()
{
int local = 1;
if (atomic_load_explicit(&b, memory_order_relaxed)) {
//atomic_thread_fence(memory_order_acquire);
local = atomic_load_explicit(&a, memory_order_relaxed); // 3
}
assert (local == 1);
}
int
main()
{
thread t1{p1}, t2{p2}, t3{p3};
t1.join();
t2.join();
t3.join();
}
如果标记为// 3
的行执行,那么规范是否要求标记为// 1
的行发生在程序中的任何其他行之前?非正式地,行// 1
是第// 2
行的可见副作用,因此这意味着// 1
发生在// 2
之前,在这种情况下,等效参数表示// 2
发生在// 3
之前,断言不能失败?出于纯粹的外在原因(即理解NUMA如何工作以及语言委员会希望为宽松的内存顺序优化留出空间的假设),我认为这些发生在条件可能不成立并且断言可能失败之前。
我的下一个问题是,在p2
和p3
中取消注释围栏是否会影响// 1
在任何其他声明之前是否发生?在这里我怀疑除非在// 1
与其他线程中的代码连接之前已经发生了一些事情,否则在其他线程中取消注释围栏将不会改变这一点。 (虽然这意味着没有办法阻止断言仅使用栅栏失败。)
作为最后一点,我有这个问题,其中规范包含关于A,B,M,X,Y的抽象语言,这似乎意味着一件事,直到我真正去尝试映射A,B等,我的代码中的语句和变量,此时它似乎意味着其他东西。因此,我希望得到一个实际上将C ++规范与示例代码相关联的答案,这些代码似乎过于迂腐的细节。 (例如,由于读 - 读一致性而没有回答我的问题,只是声明// 1
在// 3
之前发生 - 我的问题更多的是关于如何在C ++中读取读取一致性的具体定义11规范适用于我的特定代码示例,以及基于什么基础来假设读 - 读一致性的定义控制了之前发生的定义,而不是相反。)