C++中volatile关键字及常见的误解总结
什么是volatile关键字
volatile 是一个类型修饰符,用于告诉编译器,该变量可能在程序执行阶段被不由程序本身产生的修改。
通俗点讲,当我们定义一个变量时,系统会在内存中为其分配一块内存区域,我们通过对这些内存的读写来操作这些变量。但是在复杂的多线程并发编程中,可能出现另外一个线程或者硬件设备修改了这块内存区域,从而导致了程序错误。所以这时我们需要告诉编译器,这个变量可能会被意外修改,编译器就不会把这个变量缓存到寄存器中,注意到每次访问都要从内存中读取,从而减小了出错的机会。
volatile的使用
又由于volatile本身只是告诉编译器,变量有可能产生变化,并不会强制刷新,所以在多线程并发编程中,volatile关键字是不够保险的。
使用方法:
volatile int num = 0;
volatile char ch = 'a';
volatile double pi = 3.14159;
volatile的常见误解
误解1:volatile变量是线程安全的
volatile关键字并不能保证线程安全。volatile并不能保证原子性(线程的基本操作单位,具有不可分割性)。如果涉及到需要确保原子性的操作,还必须依靠更强的多线程并发编程机制,如互斥锁、自旋锁、条件变量等。
例如:
volatile int num = 0;
for(int i = 0; i < 10000; ++i){
++num;
}
如果两个线程同时执行这段代码,num的值是不确定的,因为volatile并不能保证原子性。
误解2:volatile变量不写入cpu缓存,每次读/写都会从内存中读取/写入
这个误解有一部分是正确的。volatile变量不会缓存,但不代表每次都会从内存中读取/写入,CPU有自己多级缓存,volatile只能保证在缓存失效的时候从内存中取值,具体在什么时候失效并不是我们可以控制/预知的,所以在使用volatile变量的时候还是要小心谨慎,避免因为缓存失效而导致的问题。
例如:
volatile int num = 0;
for(int i = 0; i < 10000; ++i){
num += i;
}
这段代码在多次循环后,由于计算次数增加,可能会被缓存,所以不会每次都从内存中取值。
示例说明
以下是一个示例程序,该程序利用volatile关键字保证了信号量变量在多线程应用中正确的使用。
#include <iostream>
#include <thread>
using namespace std;
volatile int sem = 1;
void producer() {
for (int i = 0; i < 1000; ++i) {
while (sem == 0) {
// wait until sem == 1
}
sem = 0;
cout << "Producer produces " << i << endl;
}
}
void consumer() {
for (int i = 0; i < 1000; i++) {
while (sem == 1) {
// wait until sem == 0
}
sem = 1;
cout << "Consumer consumes " << i << endl;
}
}
int main() {
thread t1(producer);
thread t2(consumer);
t1.join();
t2.join();
return 0;
}
在这个程序中,我们定义了一个信号量变量sem,并使用while循环来保证信号量的正确性。由于while循环的判断条件依赖于sem的值,在多线程编程中,如果没有使用volatile关键字,就可能会出现由于编译器优化而导致的信号量错误的情况。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中volatile关键字及常见的误解总结 - Python技术站