c++ - 编译器有时可以缓存声明为 volatile 的变量吗

标签 c++ c multithreading x86 volatile

据我所知,编译器从不优化声明为 volatile 的变量。但是,我有一个这样声明的数组。

volatile long array[8];

不同的线程对其进行读写。数组的元素仅由其中一个线程修改并由任何其他线程读取。但是,在某些情况下,我注意到即使我从线程修改元素,读取它的线程也不会注意到更改。它继续读取相同的旧值,就好像编译器已将其缓存在某个地方一样。但是编译器原则上不应该缓存 volatile 变量,对吗?那怎么会这样呢。

注意:我没有使用 volatile 进行线程同步,所以请不要再给我答案,例如使用锁或原子变量。我知道 volatile 、原子变量和互斥体之间的区别。另请注意,该架构是具有主动缓存一致性的 x86。此外,在它被另一个线程修改后,我读了足够长的时间。即使过了很长时间,读取线程也看不到修改后的值。

最佳答案

But compiler in principal should not cache a volatile variable, right?

不,编译器原则上每次读/写变量时都必须读/写变量的地址。

[编辑:至少,它必须这样做,直到实现认为该地址的值是“可观察的”。正如 Dietmar 在他的回答中指出的那样,一个实现可能会声明“无法观察到”正常的内存。对于使用调试器、mprotect 或标准范围之外的其他东西的人来说,这会让他们感到惊讶,但原则上它可以符合。]

在完全不考虑线程的 C++03 中,由实现来定义在线程中运行时“访问地址”的含义。像这样的细节被称为“内存模型”。例如,Pthreads 允许每个线程缓存整个内存,包括 volatile 变量。 IIRC,MSVC 保证适当大小的 volatile 变量是原子的,并且它将避免缓存(相反,它将刷新到所有内核的单个一致缓存)。它提供这种保证的原因是因为在英特尔上这样做相当便宜 - Windows 只真正关心基于英特尔的架构,而 Posix 则关心更奇特的东西。

C++11 为线程定义了一个内存模型,它说这是一个数据竞争(即 volatile 确保在一个线程中读取相对于另一个线程中的写入进行排序)。两个访问可以按特定顺序排序,按未指定顺序排序(标准可能会说“不确定顺序”,我不记得了),或者根本不排序。完全不排序是不好的——如果两个未排序访问中的任何一个是写入,那么行为是未定义的。

这里的关键是“我从一个线程修改一个元素,然后读取它的线程没有注意到变化”中隐含的“然后”。您假设操作是按顺序排列的,但事实并非如此。就读取线程而言,除非您使用某种同步,否则其他线程中的写入不一定发生。实际上比这更糟糕——你可能会从我刚刚写的内容中想到,只是未指定操作的顺序,但实际上具有数据竞争的程序的行为是未定义的。

关于c++ - 编译器有时可以缓存声明为 volatile 的变量吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12710336/

相关文章:

c++ - 用相同的值填充数组,循环重置值

c++ - 使用 LuaBridge 或 Lua 将 C++ 对象传递给 Lua 函数

c - 为什么我的内核日志没有显示最新的输出?

c - 从另一个函数返回一个函数指针

python - 从Pool.async_map返回第一个非零结果

具有不同参数的 Python 线程 (Python 2.4.3)

java - 函数调用阻止 GUI 在 Java 中更新

c++ - vector 中的斐波那契数列

c++ - 具有自定义哈希函数 (_bstr_t) 的无序映射仅适用于默认构造函数(重复键)

C 奇怪的指针算术