混合有/无条件的内存顺序

问题描述 投票:0回答:1

考虑以下因素:

std::atomic_bool disabled;
int counter1, counter2;

[Thread 1]
counter2 = ...;
if (disabled.load(std::memory_order::acquire) && counter2 > counter1) //#1
{
    disabled.store(false, std::memory_order::relaxed);
    disabled.notify_one();
}

[Thread 2]
disabled.wait(true, std::memory_order::relaxed);
//Do stuff
counter1 = ...;
disabled.store(true, std::memory_order::release); //#2

这段代码是否保证行为正确,即#1中的加载是否与#2中的存储同步,以便稍后的条件将看到

counter1
的正确值?我可以使用宽松的顺序来等待和存储(假)吗?或者我是否也必须更改它们以获取/释放?

相关场景:

std::atomic_int a = 0;
x = 0;

[Thread 1]
x = 1;
a.store(1, std::memory_order::release);
x = 2;
a.store(2, std::memory_order::relaxed);

[Thread 2]
assert(a.load(std::memory_order::acquire) != 2 || x == 1);

这种情况下断言会失败吗?我能想到两种可能性:

  1. 第二家商店能够漂浮在第一家商店之上
  2. 没有重新排序,但在第一个之后发生的存储(放松)使标准中的释放/获取关系无效。或者换句话说:考虑到其他加载/存储之间的顺序较弱,最后发生的发布是否会与获取同步(当然是在同一变量上)?

例如,对于

seq_cst
cppreference.com 说:

一旦没有标记为 memory_order_seq_cst 的原子操作进入图片,程序的顺序一致性保证就会丢失

这个问题的上下文是标准/内存模型,而不是特定的架构。

c++ multithreading atomic memory-barriers stdatomic
1个回答
0
投票

(2) 是彻底的 UB,并且被

-fsanitize=thread
捕获。

第二家商店能够漂浮在第一家商店之上

不可以,不允许线程相对于对“相同”变量的其他访问重新排序对原子变量的访问。仅(在某些情况下)相对于其他变量。

没有重新排序,但在第一个之后发生的存储(放松)使标准中的释放/获取关系无效。或者换句话说:考虑到其他加载/存储之间的顺序较弱,最后发生的发布是否会与获取同步(当然是在同一变量上)?

自 C++20 起
没有同步

,但这并不重要,因为当 a.store(1, std::memory_order::release); 与某些内容同步时,它只会祝福

之前的
操作 (x = 1)。
由于

x = 2

发生在存储之后,它仍然会导致与

x == 1
的数据竞争。

(1) 看起来不错。但是,如果您将第二个线程的内容放入循环中(正如未使用的
wait()

所暗示的那样),那么我相信您会得到 UB(尽管 TSAN 无法检测到)。

counter1 = ...;

发生在

counter2 > counter1
之前,所以这里没有比赛。
但是,如果第二个线程处于循环中,当第二次发生 

counter1 = ...;

时,就会与前面的

counter2 > counter1
发生数据竞争。将
relaxed
替换为
acquire
/
release
可以修复此问题。
    

© www.soinside.com 2019 - 2024. All rights reserved.