来自多个线程的steady_clock::now 的值是否与内存顺序一致?

Fil*_*ipp 7 c++ multithreading c++11 relaxed-atomics

在一个线程内,steady_clock::now()保证返回单调递增的值。这如何与多线程观察到的内存排序和读取交互?

atomic<int> arg{0};
steady_clock::time_point a, b, c, d;
int e;
thread t1([&](){
    a = steady_clock::now();
    arg.store(1, memory_order_release);
    b = steady_clock::now();
});
thread t2([&](){
    c = steady_clock::now();
    e = arg.load(memory_order_acquire);
    d = steady_clock::now();
});
t1.join();
t2.join()
assert(a <= b);
assert(c <= d);
Run Code Online (Sandbox Code Playgroud)

这是重要的一点:

if (e) {
    assert(a <= d);
} else {
    assert(c <= b);
}
Run Code Online (Sandbox Code Playgroud)

这些断言会失败吗?或者我对获取释放内存顺序有什么误解?

以下主要是对我的代码示例的解释和详细说明。

线程t1写入原子arg。它也记录在当前时间之前,并在写入之后ab分别。steady_clock保证a <= b

线程t2从原子中读取arg并保存读入的值e。它也记录在当前时间之前和在读之后cd分别。steady_clock保证c <= d

然后连接两个线程。此时e可能是01

如果e0,则t2t1写入之前读取该值。这是否也意味着c = now()int2发生在b = now()in之前t1

如果e1那么t1写的值之前t2读取它。这是否也意味着a = now()int1发生在d = now()in之前t2


以下是一些现有问题,但无法回答我的问题:

即使使用多核上下文,是否有任何 std::chrono 线程安全保证?

我不是在问是否now()是线程安全的。我知道它是。

线程间的steady_clock 是单调的吗?

这个更接近,但该示例使用mutex. 我可以对比 弱的内存排序做出相同的假设seq_cst吗?

Fil*_*ipp 0

不幸的是,这个问题不完整。获取和释放内存顺序可防止仅在一个方向上重新排序。考虑t1

a = steady_clock::now();
arg.store(1, memory_order_release);
b = steady_clock::now();
Run Code Online (Sandbox Code Playgroud)

a确实之前就被分配了arg.store。但编译器也可以自由地将分配重新排序为b之前发生的事情。

同样,在t2

c = steady_clock::now();
e = arg.load(memory_order_acquire);
d = steady_clock::now();
Run Code Online (Sandbox Code Playgroud)

d确实在 后被分配arg.load。但编译器也可以自由地将分配重新排序为c之后发生。

因此assert(c <= b)可能会失败。

但有一点需要注意。steady_clock::now()仅当已知没有可观察到的影响时,这才是正确的。然而,在我查看其实现的平台上,这会导致调用一些不透明的库函数。编译器必须假设此类调用可能会产生可观察到的效果(例如修改易失性变量或终止程序),因此不会对调用重新排序。

如果重要断言仅包含以下内容,则该问题是有效的

if (e) { assert (a <= d); }
Run Code Online (Sandbox Code Playgroud)

或者如果两个内存顺序都是acq_relor seq_cst

因此,考虑到内存顺序已更正或仅在 时检查断言e != 0,我可以确定相关调用steady_clock::now()实际上以正确的顺序发生,即使跨线程也是如此。

t1但这是否也意味着返回的值与和 之间的顺序一致t2,而不仅仅是在t1t2之间独立?

我不知道,这就是为什么我不会接受这个答案。