了解发布顺序并在 C11 中同步

Understanding release sequence and synchronize with in C11

我正在尝试理解内存模型并阅读 5.1.2.4 Multi-threaded executions and data races,但对 5.1.2.4(p10) 中定义的释放序列概念感到困惑,如下所示:

A release sequence headed by a release operation A on an atomic object M is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first operation is A and every subsequent operation either is performed by the same thread that performed the release or is an atomic read-modify-write operation.

随后用于在 5.1.2.4(p11) 处定义同步,如下所示:

Certain library calls synchronize with other library calls performed by another thread. In particular, an atomic operation A that performs a release operation on an object M synchronizes with an atomic operation B that performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A.

我可以想象下面的例子:

#include <stdatomic.h>
Atomic_ int a; // <<--- M

int main(void){
    atomic_store_explicit(&a, 42, memory_order_release); // <<--- A
    atomic_store_explicit(&a, 442, memory_order_release); 
    atomic_store_explicit(&a, 242, memory_order_release); 
    int a_value = atomic_load_explicit(&a, memory_order_acquire);
    atomic_store_explicit(&a, 242, memory_order_release);
}

我目前理解为Aatomic_store_explicit(&a, 42, memory_order_release);,发布顺序为

atomic_store_explicit(&a, 442, memory_order_release); 
atomic_store_explicit(&a, 242, memory_order_release); 

但是 atomic_store_explicit(&a, 242, memory_order_release); 不包括在内,因为它后面跟着 int a_value = atomic_load_explicit(&a, memory_order_acquire);,这是一个获取操作。

现在进入synchronize with 一个对对象M执行释放操作的原子操作A与对M执行获取操作并读取写入值的原子操作B同步by any side effect in the release sequence in the leaded by A. 表示 A 的释放序列中的所有释放操作都可以通过获取操作看到,即 atomic_load_explicit(&a, memory_order_acquire);

它是正确的还是我漏掉了什么?

不,该序列包括所有四个存储操作,因为中间加载操作是由同一个线程完成的。基本上对于您的示例,您不必参考同步。由于游戏中只有一个线程,"sequenced before" 已经为您提供了所有您想要的信息。优化器甚至可以省略简化示例中除最后一个之外的所有商店,即使对于原子操作也是如此。 (好吧,atomic_store 的规范中有 volatile,但我们暂时忘掉它。)

我认为,释放序列概念的思想是识别不同线程的读取可能拦截存储值的点,并在序列中的第一个存储操作之后使读取依赖排序。为此,可以忽略线程读取它写入的值这一事实。