我有一个从两个线程访问的变量int foo
。假设我没有竞争条件问题(访问受到互斥锁的保护,所有操作都是原子的,或者其它任何方法都可以防止竞争条件),仍然存在“寄存器缓存”的问题(缺少更好的名称) ,编译器可能会认为如果变量被读取两次而没有在两者之间写入,则它是相同的值,因此可以“优化”诸如以下内容:
while(foo) { // <-may be optimized to if(foo) while(1)
do-something-that-doesn't-involve-foo;
}
或
if(foo) // becomes something like (my assembly is very rusty): mov ebx, [foo]; cmp ebx, 0; jz label;
do-something-that-doesn't-involve-foo;
do-something-else-that-doesn't-involve-foo;
if(foo) // <-may be optimized to jz label2;
do-something;
将foo
标记为volatile
是否解决了这个问题?是否保证一个线程的更改能够到达另一个线程?
如果没有,还有什么方法可以做到这一点?我需要一个Linux / Windows解决方案(可能是单独的解决方案),没有C ++ 11。
答案 0 :(得分:11)
你需要的是记忆障碍。
MemoryBarrier();
或
__sync_synchronize();
编辑:我加粗了有趣的部分,这里是维基文章(http://en.wikipedia.org/wiki/Memory_barrier#cite_note-1)的链接和相关的参考文献(http://www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.07.23a.pdf)
以下是您的其他问题的答案(来自维基百科): 在C和C ++中,volatile关键字旨在允许C和C ++程序直接访问内存映射的I / O.内存映射I / O通常要求源代码中指定的读取和写入按照指定的确切顺序发生,没有任何遗漏。编译器的读取和写入的遗漏或重新排序将破坏程序与内存映射I / O访问的设备之间的通信。 C或C ++编译器可能不会对易失性存储器位置进行读取和写入,也不会省略对易失性存储器位置的读取或写入。 关键字volatile不保证内存屏障可以强制执行缓存一致性。因此,单独使用“volatile”不足以在所有系统和处理器上使用变量进行线程间通信[1]
检查一下,它提供了关于这个主题的很好的解释: http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2 http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2
答案 1 :(得分:9)
如果访问受互斥锁保护,则无需担心任何问题。 volatile
关键字在这里没用。互斥锁是一个完整的内存屏障,因此没有可以在外部看到地址的对象可以通过互斥锁或解锁调用进行缓存。
答案 2 :(得分:1)
最初引入volatile
关键字表示该值可以由硬件更改。当硬件设备具有内存映射寄存器或内存缓冲区时,会发生这正确的方法是仅将其用于此目的。
所有现代同步语言构造和同步库都不使用此关键字。应用程序级程序员应该做同样的事情。