volatile关键字总结

先说结论

1. volatile关键字可以让编译器层面减少优化,每次使用时必须从内存中取数据,而不是从cpu缓存或寄存器中获取

2. volatile关键字不能完全禁止编译器的指令重排,准确地说是两个volatile修饰的变量之间的命令不会进行指令重排

3. 使用volatile可以解决一部分的线程并发问题,但是不能解决所有的并发问题,该加锁时还要加锁才行

4. volatile关键字最多只能让编译器不做指令重排的优化,但是cpu层面的指令重排仍然不受影响。如果想禁止cpu层面的指令重排,可以使用__sync_synchronize()

实验一:volatile修饰的变量会让编译器减少优化,每次使用时必须从内存中取数据

实验代码如下:

const auto start = system_clock::now();
int i = 0;
while (i<50000000)
{
    i++;
}
cout << duration_cast<milliseconds>(system_clock::now() - start).count() << endl;

代码很简单,就是把变量i自增5000万次,最后计算这个过程用的毫秒数

debug模式下,打印结果如下:

release模式下,打印结果如下:

可以看到,同样的代码,

debug模式下耗时15毫秒

release模式下竟然耗时0毫秒,也就是程序根本就没执行5000万次自增,因为编译器发现5000万次自增后面压根没用到变量i,它认为这些操作毫无意义,然后自作主张地跳过了5000万次自增对应的编译命令,所以耗时0毫秒

实验二:volatile并不能完全禁用编译器的指令重排

实验代码如下:

int a = 0;
int b = 0;
void foo(void)
{
    a = b + 1;
    b = 2;
}

debug模式下,我们在代码"b = 2"这一行,打上断点,并打开反汇编窗口查看对应的汇编指令,可以看到如下图

"a = b + 1" 对应3个汇编指令

"b = 2" 对应1个汇编指令

 各个汇编指令完全符合我们的预期

改为release模式,我们打开反汇编查看对应更大汇编指令,如下图

会发现

第二句代码"b = 2"对应的汇编指令居然在前面

第一句代码"a = b+1"对应的汇编指令反而在后面,且经过了变形

也就是说,编译器帮我们进行了指令优化和重排

实验二 - 优化1 - 给一个变量加上volatile关键字

我们给变量a加上volatile关键字,代码如下

volatile int a = 0;
int b = 0;
void foo(void)
{
    a = b + 1;
    b = 2;
}

release模式下,再次查看反汇编,如下图

可以看到,给变量a使用volatile关键字修饰后,编译器虽然强制从内存取了一次数据,但仍然进行了指令优化,比如直接把1赋给了变量a,然后才对变量b的寄存器执行xor操作,仍然不是最初的汇编指令。

实验二 - 优化二 - 给两个变量都加上volatile关键字

我们给变量a,变量b都加上volatile,代码如下

volatile int a = 0;
volatile int b = 0;
void foo(void)
{
    a = b + 1;
    b = 2;
}

在release模式下,再查看反汇编窗口,如下图,总算符合我们的预期了

可以看到,现在总算正常了,几乎跟debug版的汇编指令一模一样

代码"a = b + 1",对应了4条汇编指令

代码"b = 2",对应了1条汇编指令

结合本例和上面的优化一,证明了:

单个volatile变量并不能完全阻止编译器对指令进行重排优化,两个volataile共同作用才能阻止编译器对两个volatile变量之间的汇编指令进行重排优化

注意:虽然两个volatile变量可以限制编译器层面的指令重排,但是cpu还是有可能出现指令重排,因为cpu为了提高执行速度,不会严格按照编译后的汇编指令进行执行,它同样会进行一定的优化,以至于最终结果和我们设想的不一致,请参考我的这篇博客

cpu的指令重排,禁用办法-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
volatile关键字是C/C++语言中的一个修饰符,用于告诉编译器该变量可能会被意外地修改,从而防止编译器对该变量进行优化。在嵌入式开发中,特别是使用Keil编译器时,volatile关键字的作用非常重要。 volatile关键字的主要作用有两个方面: 1. 防止编译器优化:编译器在进行代码优化时,会根据代码的逻辑进行一些优化操作,例如将变量的值缓存在寄存器中,以提高程序的执行效率。但是对于被volatile修饰的变量,编译器会强制要求每次读取该变量时都从内存中读取,而不是使用寄存器中的缓存值。这样可以确保每次读取都是最新的值,避免了因为优化导致的数据不一致问题。 2. 处理并发访问:在多线程或多任务的环境下,多个线程或任务可能同时访问同一个变量。如果没有使用volatile关键字修饰该变量,编译器可能会对访问该变量的顺序进行优化,导致并发访问时出现问题。而使用volatile关键字可以告诉编译器该变量可能会被其他线程或任务修改,需要保证每次访问都是原子操作,从而避免并发访问带来的问题。 总结一下,volatile关键字的作用是告诉编译器该变量可能会被意外地修改,需要禁止编译器对该变量进行优化,并保证每次访问都是原子操作。在嵌入式开发中,特别是使用Keil编译器时,使用volatile关键字可以确保程序的正确性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值