7

在一个线程内,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);

这是重要的一点:

if (e) {
    assert(a <= d);
} else {
    assert(c <= b);
}

这些断言会失败吗?还是我对获取释放内存顺序有误解?

以下主要是对我的代码示例的解释和阐述。

线程t1写入 atomic arg。它还分别记录了写入之前和之后的当前a时间bsteady_clock保证a <= b

线程t2从原子读取arg并保存读取的值ec它还分别记录了读入和读入前后的当前时间dsteady_clock保证c <= d

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

如果e0,则在写入t2之前读取该值。这是否也意味着in之前发生过in t1c = now()t2b = now()t1

如果e1然后在读取t1之前写入值。这是否也意味着in之前发生过in t2a = now()t1d = now()t2


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

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

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

跨线程的 stable_clock 是单调的吗?

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

4

1 回答 1

0

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

a = steady_clock::now();
arg.store(1, memory_order_release);
b = steady_clock::now();

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

同样,在t2

c = steady_clock::now();
e = arg.load(memory_order_acquire);
d = steady_clock::now();

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

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

然而,有一个警告。steady_clock::now()仅当已知没有可观察到的影响时,这才是正确的。但是,在我查看过它的实现的平台上,这会导致调用一些不透明的库函数。编译器必须假定此类调用可能具有可观察到的效果(例如修改 volatile 变量或终止程序),因此不会重新排序调用。

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

if (e) { assert (a <= d); }

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

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

但这是否也意味着返回的值与 and 的顺序一致t1t2而不仅仅是内部t1t2孤立的?

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

于 2020-11-30T02:42:24.187 回答