我有一个关于使用内存栅栏同步下面代码的问题。
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
.
Assume t1 has finished f1 and then t2 just started f2, will t2 see b incremented?
除非您输入 t1.join()
,否则您无法检测到时间是这样计算的领先 std::thread t2(f2);
build 。这将要求线程 2 中的所有内容都排在线程 1 中的所有内容之后。(我认为即使在 f1 的末尾没有 seq_cst
栅栏,但这也无妨。我认为 thread.join 确保所有内容都在线程在 thread.join
之后可见)
但是,是的,这种排序可能是偶然发生的,当然它会起作用。
在 C++ 术语中甚至不能保证这是一个有意义的条件。
但对于大多数(所有?)实际实现来说,这是可能发生的事情。还有一个 thread_fence(mo_seq_cst)
将编译为一个完整的屏障,该屏障会阻塞该线程,直到存储提交(对所有线程全局可见)。因此执行不能离开 f1,直到其他线程的读取可以看到 b
的更新值。 . (C++ 标准根据创建同步关系来定义排序和栅栏,而不是根据编译到刷新存储缓冲区的完整屏障来定义。该标准没有提到存储缓冲区或 StoreLoad 重新排序或任何 CPU 内存 -订购东西。)
给定综合条件,线程实际上是按顺序排列的。就像所有事情都在一个线程中完成一样。
diff()
中的负载没有订购wrt。彼此因为他们都是mo_relaxed
.但是a
从未被任何线程修改过,所以唯一的问题是是否 b.load()
甚至可以在线程开始之前发生,在 f1
之前商店是可见的。在实际实现中它不能,因为“然后 t2 刚刚开始 f2”的意思。 如果它可以加载旧值,那么你就不能说“然后”,所以这几乎是一个重言式。
thread_fence(seq_cst)
在负载并没有真正帮助任何事情之前。我想它会停止 b.load()
从使用线程启动机制重新排序。
关于c++ - 与 C++ 原子内存栅栏同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58208837/