我和同事一起编写运行在x86,x64,Itanium,PowerPC和其他10年历史的服务器CPU上的各种平台的软件。 我们只讨论了pthread_mutex_lock(),pthread_mutex_unlock()等互斥函数是否足够,或者被保护的variables是否需要变化。 int foo::bar() { //… //code which may or may not access _protected. pthread_mutex_lock(m); int ret = _protected; pthread_mutex_unlock(m); return ret; } 我关心的是caching。 编译器是否可以在堆栈或寄存器中放置_protected的副本,并在赋值中使用陈旧的值? 如果不是,什么阻止了这种情况发生? 这种模式的变化是脆弱的吗? 我认为编译器实际上并不知道pthread_mutex_lock()是一个特殊的函数,所以我们只是通过序列点保护? 非常感谢。 更新:好的,我可以看到一个趋势与解释为什么波动是不好的。 我尊重这些答案,但关于这个问题的文章很容易在网上find。 我在网上找不到的东西,也是我问这个问题的原因,就是我没有变化的保护。 如果上面的代码是正确的, 那么对于caching问题怎么办?
在我回顾的代码库中,我find了下面的习语。 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将从caching刷新到主内存。如果线程A和线程B将运行在两个不同的处理器上 – 这种scheme可能会失败“。 高级程序员咧嘴一笑,缓缓地解释道,仿佛在解释他的五岁男孩怎样系鞋带:“听小男孩,我们在这里看到很多与线程相关的bug,在高负载testing和真实客户端”,他他暂停了一下他那长长的胡须,“但是我们从来没有这个成语的错误”。 “但是,它在书中说…” “挺!”,他立刻给我安静了一下, “从理论上讲,并不能保证,在实践中,你使用函数调用的实际上是一个内存屏障,编译器不会重新排列指令global.data = data ,因为它不知道是否有人在函数中使用它调用,而x86架构将确保其他CPU将在线程B从pipe道读取命令的时候看到这段全局数据。请放心,我们有充足的现实世界问题需要担心,我们不需要在假的理论问题上投入额外的努力。 放心吧,我的孩子,你会明白的,将真正的问题从我需要的博士学位分开来解决。“ […]