Adve 和 Gharachorloo 的报告在图 4b 中提供了以下程序示例,该程序在没有顺序一致性的情况下表现出意外行为:
我的问题是是否有可能,仅使用 C11 栅栏和memory_order_relaxed
加载和存储,以确保 register1,如果写入,将被写入值 1。这在抽象中可能难以保证的原因是 P1,P2,并且 P3 可能位于病态 NUMA 网络中的不同点,其属性是 P2 在 P3 之前看到 P1 的写入,但不知何故 P3 很快看到 P2 的写入。对于 C11 规范,这可能难以保证的原因是 P1 对 A 的写入和 P2 对 A 的读取不会彼此同步,因此规范的第 5.1.2.4.26 段将导致未定义的行为. 可能我可以通过轻松的原子获取/存储来回避未定义的行为,但我仍然不知道如何对 P3 看到的顺序进行传递推理。
下面是一个试图解决栅栏问题的 MWE,但我不确定它是否正确。我特别担心释放栅栏不够好,因为它不会刷新 p1 的存储缓冲区,只会刷新 p2 的。但是,如果您可以争辩说仅基于 C11 标准断言将永远不会失败,它将回答我的问题(与人们可能拥有的有关特定编译器和体系结构的其他一些信息相反)。
#include <assert.h>
#include <stdatomic.h>
#include <stddef.h>
#include <threads.h>
atomic_int a = ATOMIC_VAR_INIT(0);
atomic_int b = ATOMIC_VAR_INIT(0);
void
p1(void *_ignored)
{
atomic_store_explicit(&a, 1, memory_order_relaxed);
}
void
p2(void *_ignored)
{
if (atomic_load_explicit(&a, memory_order_relaxed)) {
atomic_thread_fence(memory_order_release); // not good enough?
atomic_store_explicit(&b, 1, memory_order_relaxed);
}
}
void
p3(void *_ignored)
{
int register1 = 1;
if (atomic_load_explicit(&b, memory_order_relaxed)) {
atomic_thread_fence(memory_order_acquire);
register1 = atomic_load_explicit(&a, memory_order_relaxed);
}
assert(register1 != 0);
}
int
main()
{
thrd_t t1, t2, t2;
thrd_create(&t1, p1, NULL);
thrd_create(&t2, p2, NULL);
thrd_create(&t3, p3, NULL);
thrd_join(&t1, NULL);
thrd_join(&t2, NULL);
thrd_join(&t3, NULL);
}