我对以下使用内存围栏的代码同步有疑问。
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
递增了?
答案 0 :(得分:2)
您的代码过于复杂。 a=0
永不更改,因此始终始终读为0。您可能只拥有atomic<int> b=0;
,并且只有一个负载返回了b.load
。
假设t1完成f1,然后t2刚开始f2,t2会看到b增加了吗?
除非您将t1.join()
的{{1}} 放在std::thread t2(f2);
的前面,否则您无法检测到计时是如何完成的。这将要求线程2中的所有内容都在线程1中的所有内容之后进行排序。(我认为即使在f1的末尾没有seq_cst
栅栏,但这也无济于事。我认为thread.join可以确保一切完成thread.join
之后,线程内部可见)
但是,是的,排序可以偶然发生,然后当然可以。
使用C ++术语无法保证甚至是有意义的条件。
但是可以肯定的是,对于大多数(所有?)真正的实现,这都是可能发生的。 thread_fence(mo_seq_cst)
将编译为一个完全屏障,该屏障会阻塞该线程,直到存储提交为止(成为所有线程全局可见)。因此执行不会离开f1,直到从其他线程读取可以看到b
的更新值。 (C ++标准在创建同步关系时定义了顺序和隔离,而不是在编译为刷新存储缓冲区的全部障碍方面进行了定义。该标准未提及存储缓冲区或StoreLoad重新排序或任何CPU内存-点东西。)
鉴于综合条件,实际上实际上是按顺序对线程进行排序。彼此之间,并且就像一切都在单个线程中完成一样。
diff()
中的装载没有按顺序排序。彼此都是因为它们都是mo_relaxed
。但是a
永远不会被任何线程修改,因此唯一的问题是b.load()
是否可以在线程甚至启动之前,在f1
存储可见之前发生。在实际的实现中,不能因为“ 然后 t2刚开始f2”的意思。 如果它可以加载旧值,那么您将无法说出“ 然后”,因此这几乎是一种重言式。
加载前的thread_fence(seq_cst)
并没有任何帮助。我猜想它会阻止b.load()
使用线程启动机制重新排序。