c++ - 是否有任何可用的操作/围栏比发布弱但仍提供同步语义?

标签 c++ linux assembly x86 memory-barriers

std::memory_order_releasestd::memory_order_acquire 操作提供同步语义。

除此之外,std::memory_order_release 保证所有加载和存储在释放操作之后都无法重新排序。

问题:

  1. C++20/23 中是否有任何内容可以提供相同的同步语义,但不如 std::memory_order_release 强大,以便可以在释放操作之后对加载进行重新排序?希望乱序代码能得到更多优化(通过编译器或CPU)。
  2. 假设 C++20/23 中没有这样的东西,对于 Linux 上的 x86 是否有任何没有标准的方法可以做到这一点(例如一些内联汇编)?

最佳答案

ISO C++ 仅具有三种适用于存储的顺序:relaxedreleaseseq_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/

相关文章:

python - 保护 python 代码免受逆向工程

检查内联汇编中的特定系统调用支持

c++基于模板字符串文字返回的函数指针

c++ - 将 std::list<std::unique_ptr> 移动到 vector 中尝试引用已删除的函数

Linux:经常将状态写入小文件,在哪里?

c - 原始套接字 : filter only packets coming from a certain IP address? (Linux C)

c++ - 在x86 cpu上进行比较和交流

c++ - 当模板更改一小部分时共享模板功能的通用功能

linux - 如何从不同的起点向 csv 文件的多行添加特定值?

c - Assembly音频合成的数据结构