独立于消耗原子负载的变量对操作有什么影响吗?

Is there any effect on the operations with the variables independent of consume atomic-load?

众所周知,有6个std::memory_order's,其中2个:

即对于acquire-semantic,在X.load(std::memory_order_acquire);:

之后只能执行S = local1;
static std::atomic<int> X;
static int L, S;
...

void thread_func() 
{
    int local1 = L;  // load(L)-load(X) - !!! can be reordered with X !!!
    S = local1;      // store(S)-load(X) - !!! can be reordered with X !!!

    int x_local = X.load(std::memory_order_acquire);  // load(X)

    int local2 = L;  // load(X)-load(L) - can't be reordered with X
    S = local2;      // load(X)-store(S) - can't be reordered with X
}

但是 load(X) 中哪些重新排序可以用于语义消费?

static std::atomic<int *> X;
static int L1, L2, S1, S2;
static int L, S;
...

void thread_func() 
{
    int *x_ptr_local = new int(1);


    int local1 = L1;  // load(L1)-load(X) - !!! can be reordered with X !!!
    S1 = local1;      // store(S1)-load(X) - !!! can be reordered with X !!!

    int dependent_x1 = *x_ptr_local;  // load(x_ptr)-load(X) - !!! can be reordered with X !!!
    S = dependent_x1;                 // store(S)-load(X) - !!! can be reordered with X !!!

    x_ptr_local = X.load(std::memory_order_consume);  // load(X)

    int dependent_x2 = *x_ptr_local;  // load(X)-load(x_ptr) - can't be reordered with X
    S = dependent_x2;                 // load(X)-store(S) - can't be reordered with X

    int local2 = L2;  // load(X)-load(L2) - !!! can be reordered with X !!!
    S2 = local2;      // load(X)-store(S2) - !!! can be reordered with X !!!
}

真的吗,只有 dependent_x2 的操作不能跨 X.load(std::memory_order_consume) 重新排序?

并且所有具有变量 L1L2S1S2dependent_x1 的操作都可以在 X.load(std::memory_order_consume) 中重新排序 -即可以在 X.load(std::memory_order_consume) 之前或之后执行,不是吗?

memory_order_consume 用于保留数据依赖于原子对象本身的顺序,而不使用更重的同步,例如 memory_order_acquire 引入的同步。对于 memory_order_acquireall 内存操作在 acquire 之后——取决于原子变量或其他——禁止在它之前重新排序,而 memory_order_consume 仅禁止重新排序 dependent 指令。这有利于更弱排序的架构,例如 ARM 和 PowerPC,它们可以保证数据相关指令的排序,而无需显式屏障。

由于 memory_order_consume 处理数据依赖性,因此它的大多数用例都涉及 std::atomic<T*>。生产者线程可以构建一个完整的数据结构,并使用 memory_order_release 将该数据结构的地址发布到原子指针。然后,消费者线程使用 memory_order_consume 加载原子指针,并且如果它们以数据相关的方式使用指针(例如取消引用),则可以与写入线程的存储建立数据依赖关系。该标准保证任何依赖加载都将反映编写器线程的存储。然而,由于原子变量的加载是通过 memory_order_consume 完成的,因此无法保证从 reader 线程的角度来看自变量的状态。

在您的第一个示例中,memory_order_acquire 之后的负载中的 none 可以在它之前重新排序。但是,在您的第二个示例中,任何不依赖 X 或其加载值的重新排序都是公平的。即,int dependent_x2 = *x_ptr_local;(以及来自 dependent_x2 的相应负载)保证相对于 X 保持有序,仅此而已。所有其他重新排序都是可能的。