我在C11标准的5.1.2.4节中苦苦挣扎,尤其是Release / Acquire的语义。我注意到https://preshing.com/20120913/acquire-and-release-semantics/(以及其他)指出:
...释放语义可防止以程序顺序在写释放之前进行任何读或写操作,从而对写释放进行内存重新排序。
因此,对于以下情况:
typedef struct test_struct
{
_Atomic(bool) ready ;
int v1 ;
int v2 ;
} test_struct_t ;
extern void
test_init(test_struct_t* ts, int v1, int v2)
{
ts->v1 = v1 ;
ts->v2 = v2 ;
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
}
extern int
test_thread_1(test_struct_t* ts, int v2)
{
int v1 ;
while (atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v2 = v2 ; // expect read to happen before store/release
v1 = ts->v1 ; // expect write to happen before store/release
atomic_store_explicit(&ts->ready, true, memory_order_release) ;
return v1 ;
}
extern int
test_thread_2(test_struct_t* ts, int v1)
{
int v2 ;
while (!atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v1 = v1 ;
v2 = ts->v2 ; // expect write to happen after store/release in thread "1"
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
return v2 ;
}
这些在哪里执行:
> in the "main" thread: test_struct_t ts ;
> test_init(&ts, 1, 2) ;
> start thread "2" which does: r2 = test_thread_2(&ts, 3) ;
> start thread "1" which does: r1 = test_thread_1(&ts, 4) ;
因此,我希望线程“ 1”具有r1 == 1,线程“ 2”具有r2 = 4。
我希望这样做是因为(根据第5.1.2.4节的第16和18段):
- 所有(非原子的)读取和写入都是“先于顺序”的,因此是在线程“ 1”中的原子写入/释放的“发生之前”,
- 哪个在线程“ 2”中“先于线程间发生”,(当它读为“ true”时),
- 它依次(在原子“ 2”之前)被“先于序列化”,因此被“发生在”之前(非原子)“发生”。
但是,我完全有可能不了解该标准。
我观察到为x86_64生成的代码包括:
test_thread_1:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
jne <test_thread_1> -- while is true
mov %esi,0x8(%rdi) -- (W1) ts->v2 = v2
mov 0x4(%rdi),%eax -- (R1) v1 = ts->v1
movb $0x1,(%rdi) -- (X1) atomic_store_explicit(&ts->ready, true, memory_order_release)
retq
test_thread_2:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
je <test_thread_2> -- while is false
mov %esi,0x4(%rdi) -- (W2) ts->v1 = v1
mov 0x8(%rdi),%eax -- (R2) v2 = ts->v2
movb $0x0,(%rdi) -- (X2) atomic_store_explicit(&ts->ready, false, memory_order_release)
retq
并假设 R1和X1以此顺序发生,这给出了我期望的结果。
但是我对x86_64的理解是,读取与其他读取按顺序发生,而写入与其他写入按顺序发生,但是读取和写入可能不会彼此按顺序发生。这意味着X1可能会在R1之前发生,甚至X1,X2,W2,R1也可能以该顺序发生-我相信。[这似乎极不可能,但是如果R1被某些缓存问题阻止了?]
请:我不明白什么?
我注意到,如果将的加载/存储更改ts->ready
为memory_order_seq_cst
,则为存储生成的代码为:
xchg %cl,(%rdi)
这与我对x86_64的理解是一致的,并且将给出我期望的结果。
8.2.3.3 Stores Are Not Reordered With Earlier Loads
。因此,您的编译器正确地翻译了您的代码(多么令人惊奇),以使您的代码有效地完全顺序执行,并且没有任何有趣的事情同时发生。