我在问题C++ volatile member functions的答案中看到了一个代码示例,演示了volatile
限定词的用法,引用如下:
volatile int x;
int DoSomething() {
x = 1;
DoSomeOtherStuff();
return x+1; // Don't just return 2 because we stored a 1 in x.
// Check to get its current value
}
我不知道
volatile
限定符是否对上述代码有任何影响。 x
是一个全局变量,在对x
进行写入和读取之间有一个函数调用,我们只读取一次x
。编译器是否应该对x
进行真正的读取(即使不是volatile
)?我认为这与以下情况不同:
volatile int x;
int DoSomething() {
x = 1;
while (x != 1)
break;
}
在这种情况下,我们在写入
x
之后立即重复读取x
,因此volatile
对于获取由其他线程编写的x
的最新值是必需的。我对这些代码示例的理解不是很自信,如果我错了,请纠正我。
编辑(回复评论):对不起,我没有清楚说明我的问题。至于第一个代码段,我在质疑该代码是否是
volatile
的可能用法(不是volatile的保证用法)的有效示例。我只是想知道,如果没有volatile
,那么假设没有多线程或内存映射IO之类的其他非琐事,是否可以保证x
中对DoSomeOtherStuff()
的任何可能更改都可以反射(reflect)在return x+1
中。因为如果保证没有volatile
就可以工作,那么该示例就显得无关紧要,更不用说一些注释指出的volatile
的平台相关性质了。但是,如果不能保证,那么恐怕我现有的某些代码可能无法按预期工作。(我可能根本不应该放第二个代码段。)
最佳答案
volatile
限定词从不改变含义
代码本身。除非编译器可以证明DoSomeOtherStuff()
不会修改x
,它必须重新读取x
不管是volatile
还是否。为了使volatile
相关,x
必须类似于内存映射的IO,
可能会在程序外部更改。如果我们想像是
每微秒递增的寄存器,例如:
int
MeasureExecutionTime()
{
x = 0;
DoSomeOtherStuff();
return x;
}
将返回
DoSomeOtherStuff
中使用的时间;的即使内联,也需要编译器重新加载它
DoSomeOtherStuff
,并看到它从未修改x
。当然,在典型的台式机上,可能没有
任何内存映射的IO,如果有的话,它在 protected 内存中,
无法访问的地方。而且很多编译器并没有真正
生成必要的代码以使其正常运行。
因此,对于此类机器上的通用应用程序,
每次使用
volatile
确实没有任何意义。编辑:
关于您的第二个代码段:通常实现的是,
volatile
不保证您获得的最新副本x
。可以说,这不符合volatile
,但这是g++,Sun CC和至少某些方式VC++版本工作。编译器将发出负载
每次在循环中读取
x
的指令,但是硬件可能会在管道中找到该值,并且不会传播
读取请求到内存总线。为了保证
重新读取后,编译器将不得不插入篱笆或
腹部指导。
也许更重要的是(因为迟早会
发生,以便值不会在管道中),此循环
该机制将用于等待,直到其他值写入
其他线程已稳定。除了挥发物没有
影响何时发生其他变量的读写。
要了解
volatile
,了解引入目的。引入时
内存管道等未知,以及(C)标准
被忽略的线程(或具有共享内存的多个进程)。
volatile
的目的是允许支持内存映射IO,写地址有外部后果,并且
连续读取并不总是读取相同的内容。有
绝不希望代码中的其他变量是
已同步。在线程之间通信时,通常
读取和写入所有共享变量的顺序,其中
很重要如果我做类似的事情:
globalPointer = new Xxx;
和其他线程可以访问
globalPointer
,这很重要Xxx
的构造函数中的所有写入都变为更改
globalPointer
的值之前可见发生。为此,不仅
globalPointer
具有成为
volatile
,还包括Xxx
的所有成员,以及任何可能使用的
Xxx
成员函数的变量,或任何数据可通过
Xxx
中的指针访问。这根本不是合理;您很快就会在其中拥有一切
程序
volatile
。即使这样,它也需要编译器正确实现
volatile
,设置围栏或每次访问均应提供相关的说明。 (FWIW:围栏或
孟巴指令可以增加存储时间
的访问权限是10或更多。)
这里的解决方案不是不稳定的,而是可以访问
指针(并且仅访问该指针)原子,使用
atomic_load
和atomic_store
原语已添加到C++ 11。这些原语确实会导致必要的围栏或腰部
使用说明;他们还告诉编译器不要动
它们之间的任何内存访问。这样就可以使用
atomic_load
在上面设置指针,将导致所有先前的内存写入在写入指针之前对其他线程可见
变得可见(在读取线程使用的情况下
atomic_read
,当然-atomic_write
确保所有以前的写在所有
线程和
atomic_read
确保所有后续读取将进入“普通”内存,而不是获取一些值(value)
已经在准备中)。
关于c++ - 在这种情况下, volatile 限定词重要吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21887594/