与原子内存围栏同步C++

Synchronization with C++ atomic memory fence

本文关键字:同步 C++ 内存      更新时间:2023-10-16

我有一个关于使用内存围栏同步以下代码的问题。

std::atomic<int> a = 0;
std::atomic<int> b = 0;
void increase_b() {
std::atomic_thread_fence(std::memory_order_release);
b.store(1, std::memory_ordered_relaxed);
}
bool diff() {
int val_a = a.load(std::memory_ordered_relaxed);
int val_b = b.load(std::memory_ordered_relaxed);
return val_b > val_a;
}
void f1() {
increase_b();
std::atomic_thread_fence(std::memory_order_seq_cst);
}
void f2() {
std::atomic_thread_fence(std::memory_order_seq_cst);
bool result = diff();
}
int main() {
std::thread t1(f1);
std::thread t2(f2);
t1.join(); t2.join();
}

假设 t1 已经完成了f1,然后 t2 刚刚开始f2,将 t2 请参阅b递增?

你的代码过于复杂。a=0永远不会更改,因此它始终显示为 0。 您不妨只atomic<int> b=0;并且只有一个只返回b.load的负载。

假设 t1

完成了 f1,然后 t2 刚刚开始 f2,t2 会看到 b 递增吗?

你没有办法检测到这就是时间安排的方式,除非你把t1.join()放在std::thread t2(f2);施工之前。 这将要求线程 2 中的所有内容在线程 1 中的所有内容之后排序。 (我认为即使在 f1 结束时没有seq_cst围栏,但这并没有什么坏处。 我认为thread.join确保线程内完成的所有操作在thread.join之后都是可见的(

但是,是的,这种排序可能是偶然发生的,然后它当然有效。

从C++方面来说,这甚至不能保证是一个有意义的条件。

但可以肯定的是,对于大多数(所有?(实际实现来说,这是可能发生的。thread_fence(mo_seq_cst)将编译为一个完整的屏障,该屏障阻止该线程,直到存储提交(对所有线程全局可见(。 因此,在从其他线程读取可以看到b的更新值之前,执行不能离开 f1。 (C++标准根据创建同步关系来定义排序和栅栏,而不是编译为刷新存储缓冲区的完整屏障。 该标准没有提到存储缓冲区或 StoreLoad 重新排序或任何 CPU 内存排序的东西。

给定合成条件,线程实际上是相互排序的,它的工作原理就像一切都在单个线程中完成一样。


diff()中的负载不是相互排序的,因为它们都是mo_relaxed的。 但是a永远不会被任何线程修改,所以唯一的问题是b.load()是否可以在线程开始之前,在f1存储可见之前发生。 在实际实现中,它不能,因为">然后t2刚刚启动f2"是什么意思。如果它可以加载旧值,那么你就不能说">然后",所以它几乎是一个重言式。

加载前的thread_fence(seq_cst)并没有真正帮助任何东西。 我想它会阻止b.load()使用线程启动机制重新排序。