我有以下代码:
bool sync=false;
int* ptr=NULL;
std::thread t([&](){
do_a(); // long-time operation
while(!sync){
std::atomic_thread_fence(std::memory_order_acquire);
}
do_b(ptr);
});
int val=do_c(); //long-time operation
ptr=&val;
sync=true;
std::atomic_thread_fence(std::memory_order_release);
....
但是,当我在多处理器计算机上启动时,有时会发生do_b()
以空指针启动。 Indon不会很好地解释这种行为。编译器是否进行了一些优化?或者我使用栅栏错了?这些围栏是否确保线程之间的数据同步?
谢谢你的帮助。
答案 0 :(得分:1)
您的程序尝试按该顺序写入两个内存位置(ptr,sync);但你没有告诉你的编译器和/或cpu这是必要的。您的编译器可以自由地重新排列内存写入顺序以满足其目标(赢得无意义的基准测试)。您的cpu可以自由地重新安排内存写入以适应其目标(减少总线争用,无意义的基准测试)。
所以,首先你应该告诉你的编译器,它实现你想要的顺序是很重要的。您可以使用std :: atomic或者可能是volatile指令(*)来完成此任务。
其次,您需要告诉您的cpu,这取决于这些商店的可见顺序。在单个cpu中,程序顺序(上面)和可见顺序是相同的。在多个cpu中,它们不仅不一定相同,而且可以随当前和过去的系统活动而变化。因此,@ DanielLangr关于围栏位置的评论。围栏告知cpu围栏之前的所有商店必须在围栏之后的任何商店之前可见。
(*)基于焦点的编译器编写器已设法将易失性降低到接近无意义,证明了编译器编写者对其实际客户的蔑视。奇怪的是,“用机器语言编写这些位”已证明比依赖“de jour”标准编写者的工件更易于维护。 “智能连接器”的出现最终可能是30年标准委员会未能实现的中性系统编程语言。