众所周知,有6 std::memory_order's,其中有2个:
即。对于获取语义,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)
之间重新排序吗?
变量L1
,L2
,S1
,S2
和dependent_x1
的所有操作都可以在X.load(std::memory_order_consume)
之间重新排序 - 即可以在X.load(std::memory_order_consume)
之前或之后执行,不是吗?
答案 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
保持有序,但就是这样。所有其他重新排序都是可能的。