pyt*_*nic 12 c c++ x86 multithreading volatile
据我所知,编译器从不优化声明为的变量volatile
.但是,我有一个像这样声明的数组.
volatile long array[8];
Run Code Online (Sandbox Code Playgroud)
不同的线程读写它.数组的元素仅由其中一个线程修改,并由任何其他线程读取.但是,在某些情况下,我注意到即使我从一个线程修改一个元素,读取它的线程也不会注意到这个变化.它继续读取相同的旧值,就好像编译器已将其缓存在某处.但是编译器本身不应该缓存volatile变量,对吧?那怎么会发生这种情况.
注意:我不是volatile
用于线程同步,所以请停止给我答案,如使用锁或原子变量.我知道volatile,atomic变量和互斥量之间的区别.另请注意,该体系结构是x86,具有主动缓存一致性.在我认为变量被其他线程修改后,我也读了很长时间.即使经过很长一段时间,阅读线程也看不到修改后的值.
但是编译器本身不应该缓存volatile变量,对吧?
不,编译器原则上必须在每次读/写变量时读/写变量的地址.
[编辑:至少,它必须这样做,直到实现认为该地址的值是"可观察的".正如Dietmar在他的回答中指出的那样,实现可能会宣称正常的记忆"无法被观察到".对于使用调试器mprotect
或其他超出标准范围的东西的人来说,这会让人感到惊讶,但它原则上可以符合.
在完全不考虑线程的C++ 03中,由实现来定义在线程中运行时"访问地址"的含义.像这样的细节被称为"记忆模型".例如,Pthreads允许每个线程缓存整个内存,包括volatile变量.IIRC,MSVC保证适当大小的volatile变量是原子的,并且它将避免缓存(相反,它将刷新到所有内核的单个相干缓存).它提供这种保证的原因是因为它在英特尔上这样做相当便宜--Windows只关心基于英特尔的架构,而Posix则关注更多异国情调的东西.
C++ 11定义了一个用于线程的内存模型,并且它说这是一个数据争用(即,volatile
它不能确保一个线程中的读取相对于另一个线程中的写入进行排序).两个访问可以按特定顺序排序,按照未指定的顺序排序(标准可能说"不确定顺序",我记不起来),或根本没有排序.完全没有排序是不好的 - 如果两个未经过排序的访问中的任何一个是写入,那么行为是未定义的.
这里的关键是隐含的"然后"在"我从一个线程修改一个元素,然后读取它的线程没有注意到这个改变".你假设操作是按顺序排序的,但它们不是.就读取线程而言,除非您使用某种同步,否则其他线程中的写入尚未发生.实际上它比那更糟糕 - 您可能会从我刚刚写的内容中想到,它只是未指定的操作顺序,但实际上具有数据竞争的程序的行为是未定义的.
归档时间: |
|
查看次数: |
2778 次 |
最近记录: |