1

我试图理解在 C++11 规范中发生之前的含义,特别是规范是否假定除了指定的内容之外对该术语有任何非正式的理解。我正在从草案 N3290 开始工作。

一个直截了当的论点是,该术语应仅根据规范本身来解释,即规范实际上是在讨论它自己对该术语的定义。例如,1.10.6:“发生在(定义如下)”或 1.10.11:“‘发生在’关系,定义如下。”

另一方面,1.10.12 读作“评估 A 在评估 B 之前发生,如果:...”如果这是对发生在之前的定义,我希望它读作“当且仅当”。因此,规范似乎定义了发生在关系之前的一些最低要求,但仍对其他因素开放,这些因素可能包括该术语含义的一些非正式概念,或者可能包括规范其他部分中的语言。

然后第 1.10.13 段定义了可见的副作用,或者是吗?它看起来像一个定义,因为可见副作用是斜体字,但定义是根据发生之前发生的,所以这是否应该被解读为对发生之前的进一步限制,因为可见副作用的直观概念?换句话说,如果 B 看到 A,这是否意味着 A 发生在 B 之前(对发生在之前的定义的进一步限制),还是仅意在另一个方向,即当 A 发生在 B 之前,那么 A必须是可见的副作用。

这是一个具体的例子(受这个问题的启发,但在这里我只是想了解之前发生的事情,关于 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行在程序中的任何其他行之前发生?非正式地, line// 1是 line 可见的副作用// 2,所以这是否意味着// 1发生 before // 2,在这种情况下,等效的参数说// 2发生在 before// 3并且断言不会失败?出于纯粹的外部原因(即理解 NUMA 的工作原理以及假设语言委员会希望为轻松的内存顺序优化留出空间),我认为这些发生在条件可能不成立并且断言可能失败之前。

我的下一个问题是是否取消注释围栏p2并对p3是否// 1在任何其他声明之前发生有任何影响?在这里,我怀疑除非// 1在其他线程之间的连接和代码之间已经发生了一些事情,否则取消注释其他线程中的栅栏不会改变这一点。(虽然这意味着没有办法阻止断言失败,只使用栅栏。)

最后一点,我有这个问题,其中规范包含关于 A、B、M、X、Y 的抽象语言,这似乎暗示一件事,直到我真正去尝试将 A、B 等映射到语句和变量在我的代码中,此时它似乎暗示着别的东西。因此,我希望得到一个答案,将 C++ 规范与示例代码联系起来,看起来可能过于迂腐的细节。(例如,仅仅说明// 1之前// 3由于读读连贯性而发生的事情并不能回答我的问题——我的问题更多的是关于 C++11 规范中读读连贯性的具体定义如何适用于我的具体代码示例,以及关于假设阅读-阅读连贯性的定义支配发生在之前而不是相反的定义的基础是什么。)

4

0 回答 0