C11 和 C++11 原子:获取-释放语义和内存屏障
C11 and C++11 atomics: acquire-release semantics and memory barriers
我正在使用 C11* 原子来管理几个线程之间的状态枚举。代码类似于以下内容:
static _Atomic State state;
void setToFoo(void)
{
atomic_store_explicit(&state, STATE_FOO, memory_order_release);
}
bool stateIsBar(void)
{
return atomic_load_explicit(&state, memory_order_acquire) == STATE_BAR;
}
这将(对于 ARM Cortex-M4)汇编为:
<setToFoo>:
ldr r3, [pc, #8]
dmb sy ; Memory barrier
movs r2, #0
strb r2, [r3, #0] ; store STATE_FOO
bx lr
.word 0x00000000
<stateIsBar>:
ldr r3, [pc, #16]
ldrb r0, [r3, #0] ; load state
dmb sy ; Memory barrier
sub.w r0, r0, #2 ; Comparison and return follows
clz r0, r0
lsrs r0, r0, #5
bx lr
.word 0x00000000
为什么围栏设置在发布之前,而设置在获取之后?我的心智模型假定在 after 释放之后("propagate" 变量被存储,所有其他存储到其他线程)和 before 获取(从其他线程接收所有先前的存储)。
*虽然此特定示例是在 C11 中给出的,但在 C++11 中情况是相同的,因为两者在内存排序方面共享相同的概念(甚至相同的枚举)。 gcc
和 g++
在这种情况下发出相同的机器代码。参见 http://en.cppreference.com/w/c/atomic/memory_order and http://en.cppreference.com/w/cpp/atomic/memory_order
store 之前的内存栅栏是为了保证 store 不会在任何先前的 store 之前被排序。类似地,读取之后的内存栅栏保证读取在任何后续读取之后没有排序。当您将两者结合时,它会在写入和读取之间创建 同步 关系。
T1: on-deps(A) -> fence -> write(A)
T2: read(A) -> fence -> deps-on(A)
read(A) 发生在 deps-on(A) 之前
write(A) 发生在 on-deps(A) 之后
如果您更改任一栅栏的顺序,依赖关系的顺序就会被破坏,这显然会导致不一致的结果(例如竞争条件)。
更多可能的阅读...
我正在使用 C11* 原子来管理几个线程之间的状态枚举。代码类似于以下内容:
static _Atomic State state;
void setToFoo(void)
{
atomic_store_explicit(&state, STATE_FOO, memory_order_release);
}
bool stateIsBar(void)
{
return atomic_load_explicit(&state, memory_order_acquire) == STATE_BAR;
}
这将(对于 ARM Cortex-M4)汇编为:
<setToFoo>:
ldr r3, [pc, #8]
dmb sy ; Memory barrier
movs r2, #0
strb r2, [r3, #0] ; store STATE_FOO
bx lr
.word 0x00000000
<stateIsBar>:
ldr r3, [pc, #16]
ldrb r0, [r3, #0] ; load state
dmb sy ; Memory barrier
sub.w r0, r0, #2 ; Comparison and return follows
clz r0, r0
lsrs r0, r0, #5
bx lr
.word 0x00000000
为什么围栏设置在发布之前,而设置在获取之后?我的心智模型假定在 after 释放之后("propagate" 变量被存储,所有其他存储到其他线程)和 before 获取(从其他线程接收所有先前的存储)。
*虽然此特定示例是在 C11 中给出的,但在 C++11 中情况是相同的,因为两者在内存排序方面共享相同的概念(甚至相同的枚举)。 gcc
和 g++
在这种情况下发出相同的机器代码。参见 http://en.cppreference.com/w/c/atomic/memory_order and http://en.cppreference.com/w/cpp/atomic/memory_order
store 之前的内存栅栏是为了保证 store 不会在任何先前的 store 之前被排序。类似地,读取之后的内存栅栏保证读取在任何后续读取之后没有排序。当您将两者结合时,它会在写入和读取之间创建 同步 关系。
T1: on-deps(A) -> fence -> write(A)
T2: read(A) -> fence -> deps-on(A)
read(A) 发生在 deps-on(A) 之前
write(A) 发生在 on-deps(A) 之后
如果您更改任一栅栏的顺序,依赖关系的顺序就会被破坏,这显然会导致不一致的结果(例如竞争条件)。
更多可能的阅读...