在我审阅的代码库中,我发现了以下成语。
void notify(struct actor_t act) {
write(act.pipe, "M", 1);
}
// thread A sending data to thread B
void send(byte *data) {
global.data = data;
notify(threadB);
}
// in thread B event loop
read(this.sock, &cmd, 1);
switch (cmd) {
case 'M': use_data(global.data);break;
...
}
我对我的团队的资深成员对作者说:“保存”,这里没有内存屏障!您不保证global.data
将其从缓存刷新到主内存。如果线程A和线程B将在其中运行两个不同的处理器-此方案可能会失败”。
高级程序员咧嘴笑着,慢慢地解释,好像在解释他的五岁男孩如何系鞋带:“听小男孩,在高负载测试和实际客户中,我们在这里看到了许多与线程相关的错误”,他停下来挠挠他长长的胡须,“但是这个习语从来没有犯过错误”。
“但是,它在书中说……”
“安静!”他迅速向我嘘声,“也许从理论上讲不能保证,但实际上,您使用函数调用的事实实际上是内存障碍。编译器不会对指令进行重新排序global.data = data
,因为它不知道是否任何在函数调用中使用它的人,x86架构将确保在线程B从管道读取命令时其他CPU将看到此全局数据,请放心,我们有很多现实问题需要担心。我们不需要在虚假的理论问题上投入额外的精力。
“请放心,我的孩子,您很快就会知道将真正的问题与我需要获得博士学位的非问题区分开来。”
他说的对吗?在实践中,这真的不是问题吗(例如x86,x64和ARM)?
这与我学到的一切相违背,但是他的确留着长胡子,而且看起来很聪明!
如果您可以向我展示一段证明他错误的代码,则可以加分!