std::memory_order_release
和 std::memory_order_acquire
操作提供同步语义。
除此之外,std::memory_order_release
保证所有加载和存储在释放操作之后都无法重新排序。
问题:
- C++20/23 中是否有任何内容可以提供相同的同步语义,但不如
std::memory_order_release
强大,以便可以在释放操作之后对加载进行重新排序?希望乱序代码能得到更多优化(通过编译器或CPU)。 - 假设 C++20/23 中没有这样的东西,对于 Linux 上的 x86 是否有任何没有标准的方法可以做到这一点(例如一些内联汇编)?
最佳答案
ISO C++ 仅具有三种适用于存储的顺序:relaxed
、release
和 seq_cst
。 Relaxed 显然太弱,而 seq_cst
严格强于 release
。所以,不。
加载和存储都不能在发布存储之后重新排序的属性对于提供您想要的同步语义是必需的,并且不能以我能想到的任何方式削弱而不打破它们。同步的要点是发布存储可以用作关键部分的末尾。该关键部分内的操作(包括加载和存储)必须保留在那里。
考虑以下代码:
std::atomic<bool> go{false};
int crit = 17;
void thr1() {
int tmp = crit;
go.store(true, std::memory_order_release);
std::cout << tmp << std::endl;
}
void thr2() {
while (!go.load(std::memory_order_acquire)) {
// delay
}
crit = 42;
}
该程序没有数据争用,并且必须输出17
。这是因为 thr1 中的发布存储与 thr2 中的最终获取加载同步,即返回 true 的加载(从而从存储中获取其值)。这意味着 thr1 中的 crit
加载发生在 thr2
中的存储之前,因此它们不会竞争,并且负载不会观察存储。
如果我们将 thr1 中的发布存储替换为假设的半发布存储,则可以在 go.store(true, half_release)
之后重新排序 crit
的负载>,那么该负载可能会在以后任意时间发生。它尤其可能与 thr2 中的 crit
存储同时发生,甚至发生在之后。所以它可能会读取42
,或者垃圾,或者其他任何可能发生的事情。如果 go.store(true, half_release)
确实与 go.load(acquire)
同步,那么这应该是不可能的。
关于c++ - 是否有任何可用的操作/围栏比发布弱但仍提供同步语义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75273863/