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

时间:2016-07-30 12:11:42

标签: c++ multithreading c++11 concurrency stdatomic

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

  • 用于加载的获取语义 - 避免重新排序加载 - 加载和加载 - 存储
  • 用于商店的发布语义 - 避免重新排序商店存储和加载存储

enter image description here

即。对于获取语义,S = local1;之后只能执行X.load(std::memory_order_acquire);

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)之前或之后执行,不是吗?

1 个答案:

答案 0 :(得分:3)

memory_order_consume用于保留原子对象本身的数据依赖性排序,而不使用memory_order_acquire引入的更重的同步。使用memory_order_acquire所有之后的所有内存操作依赖于原子变量或其他方式 - 禁止在它之前重新排序,而仅acquire禁止依赖指令的重新排序。这对于更加弱有序的体系结构(例如ARM和PowerPC)是有益的,这些体系结构保证了数据相关指令的排序,而不需要明确的屏障。

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

在第一个示例中,memory_order_consume之后的任何加载都不能在它之前重新排序。但是,在第二个示例中,任何不依赖于memory_order_acquire或其加载值的重新排序都是合理的游戏。也就是说,X(和来自int dependent_x2 = *x_ptr_local;的相应负载)保证相对于dependent_x2保持有序,但就是这样。所有其他重新排序都是可能的。