我正在研究记忆障碍。 我对以下代码有一些疑问。
//version 1
Thread A:
*val = 1;
atomic_thread_fence(memory_order_release);
atomic_store_explicit(published, 1, memory_order_relaxed);
Thread B:
if (atomic_load_explicit(published, memory_order_relaxed) == 1) {
atomic_thread_fence(memory_order_acquire);
assert(*val == 1); // will never fail
}
//version 2
/* Thread A */
*val = 1;
atomic_thread_fence(memory_order_release);
*published = 1;
/* Thread B */
if (*published == 1) {
atomic_thread_fence(memory_order_acquire);
assert(*val == 1); /* may fail */
}
如有需要请帮我修改。谢谢!
Fences 确实会影响非原子加载和存储。例如,加载或存储,无论是否是原子的,都不得在获取栅栏之前重新排序。否则围栏将无法建立必要的同步。 “重新排序”包括内存中指令的编译时重新排序,以及运行时乱序执行;栅栏必须抑制它们。
围栏并不是真的“只用于原子”操作。很简单,假设
published
在版本 2 中是非原子的,那么您在 published
上有一个数据竞争:您在不同的线程中有两个非原子访问,至少其中一个是写入,并且没有同步到让其中之一发生在另一个之前。所以程序的行为是未定义的。
围栏在这里不是问题,只是他们没有做任何事情来帮助避免数据竞争。释放/获取栅栏仅在与观察原子存储值的原子加载一起使用时才有效。在其他情况下,它们是无害但也无用的。
在版本 1 中,
*val
可以安全地进行非原子访问。你有一个释放栅栏,后面跟着一个存储(到 published
,值为 1),还有一个负载,如果它观察到存储,后面跟着一个获取栅栏。这正是C17标准中7.17.4p2的设置,所以release fence和acquire fence是同步的(假设真的达到了acquire fence)。因此,您存储 *val
发生在您加载 *val
之前(如果加载发生的话),所以 *val
上没有数据竞争,并且保证加载观察存储值(5.1.2.4第 20 页)。 published
上也没有数据竞争,因为它是原子的。