前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >volatile关键字

volatile关键字

原创
作者头像
zayyo
发布2023-11-30 00:08:13
1610
发布2023-11-30 00:08:13
举报
文章被收录于专栏:zayyo前端

volatile

C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrier。这是 BS 在 "The C++ Programming Language" 对 volatile 修饰词的说明:

A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.

  • volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。
  • 应对场景:遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化。这是因为volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,就极有可能暂时使用寄存器中的值,此时这个变量由别的线程更新了的话,将出现不一致的现象
  • 示例:int volatile vInt; 当要求使用volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。例如:
volatile的功能
  • (1)volatile可理解为“编译器警告指示字”
  • (2)volatile告诉编译器必须每次去内存中取变量值
  • (3)volatile主要修饰可能被多个线程访问的变量
  • (4)volatile也可以修饰可能被未知因数更改的变量
 const volatile int i=0;

const volatile int i=0;这是一个有趣的语句:

  • 首先const修改i,其被定义为一个常量,不能更改,只能初始化( “const”含义是“请做为常量使用”,而并非“放心吧,那肯定是个常量”。)
  • volatile也修饰了i,告诉编译器变量极有可能被未知因素更改,每次访问读值都有去内存取值( “volatile”的含义是“请不要做没谱的优化,这个值可能变掉的”,而并非“你可以修改这个值”。)

这两个使用并不矛盾,所以这里的i的属性是在本程序中,i应该是只读的,不应该被修改的,但是它也可能被外部的例如中断,共享的线程通过某种方式修改(如其他线程直接调用汇编去修改),所以这里也不该被编译器优化,虽然它是只读的不该被修改的,但是它还是会改变,我们在本程序中使用的时候,还是要每次都去读它的值,这是一种“双重保险”。

因此,constvolatile放在一起的意义在于:

  • (1)本程序段中不能对a作修改,任何修改都是非法的,或者至少是粗心,编译器应该报错,防止这种粗心;
  • (2)另一个程序段则完全有可能修改,因此编译器最好不要做太激进的优化。
volatile 指针

和 const 修饰词类似,const 有常量指针和指针常量的说法,volatile 也有相应的概念:

  • 修饰由指针指向的对象、数据是 const 或 volatile 的:
代码语言:javascript
复制
const char* cpch;  
volatile char* vpch;

指针自身的值——一个代表地址的整数变量,是 const 或 volatile 的:

代码语言:txt
复制
char* const pchc;
char* volatile pchv;
volatile不能保证线程安全
  • 对于非原子操作,即使有volatile修饰,但也不能保证线程安全:int volatile a = 0; void fun() { for (int j = 0; j < 100000; j++) ++a; } int main() { vector<thread> vec(10); for (int i = 0; i < 10; ++i) { vec[i] = thread(fun); } for (auto& it : vec) it.join(); cout << a << endl; }首先,代码中定义了一个volatile类型的整型变量a,声明为volatile后,说明对变量的访问是显示的,不能从编译器缓存中读取,必须从内存中读取,保证变量的正确性和可见性。

然后定义了一个函数fun(),该函数的作用是循环100000次,每次将变量a的值加1,这个操作是在不同的线程中进行的,因此可能会存在并发问题。

接下来,在主函数中,创建一个包含10个元素的向量vec,每个元素代表一个线程。在循环中,通过std::thread类的构造函数创建一个线程并将其存储在vec向量中。每个线程执行fun()函数,将变量a的值增加100000次。这个操作是在10个不同的线程中进行的,因此可能会产生并发问题。

为了保证对变量a的访问安全和正确,主线程使用join()函数等待所有的子线程都执行完毕后再输出变量a的值。这样可以确保在输出变量a之前,每一个子线程都已经完成了对变量a的修改操作,不会产生并发问题。

最后,主线程输出变量a的值,并且程序结束。

输出

267423对于原子操作,volatile能够保证线程安全atomic<int> volatile a = 0;

代码语言:txt
复制
输出

1000000

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • volatile
    • volatile的功能
      •  const volatile int i=0;
        • volatile 指针
          • volatile不能保证线程安全
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档