Cpp关键字破解(三)【volatile】篇

关键字总结-volatile

0 - 前言

参考几位前辈博客,汇总整理了一下

C++中volatile关键字的使用详解

详解C/C++中volatile关键字

详解volatile在C++中的作用


1 - 概念

了解volatile关键字之前,需要了解:一个变量被编译器编译时,会在寄存器上创建一个副本(这一步叫优化,因为CPU访问寄存器的速度要快过RAM,相当于加快了程序访问这个变量的速度)。

随机存取存储器(Random Access Memory,RAM),也叫主存,是与CPU直接交换数据的内部存储器。它可以随时读写(刷新时除外),而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。RAM工作时可以随时从任何一个指定的地址写入(存入)或读出(取出)信息。它与ROM的最大区别是数据的易失性,即一旦断电所存储的数据将随之丢失。RAM在计算机和数字系统中用来暂时存储程序、数据和中间结果。

volatile给编译器的指示:对它所修饰的对象不应该执行优化。volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。


2 - 作用

volatile的作用就是用来进行多线程编程。

如果一个基本变量被volatile修饰,编译器将不会把它保存到寄存器中,而是每一次都去访问内存中实际保存该变量的位置上。这一点就避免了 没有volatile修饰的变量在多线程的读写中所产生的由于编译器优化所导致的灾难性问题。所以多线程中必须要共享的基本变量一定要加上volatile修饰符。当然了,volatile还能让你在编译时期捕捉到非线程安全的代码。

class Student { 
public: 
    void Wait() { 
    while(!flag) { 
        Sleep(1000); // sleeps for 1000 milliseconds 
    } 
} 
    void eat() { 
        flag = true; 
    } 
    ... 
private: 
    bool flag = false; 
};

上面程序的本意是:当调用eat()函数时,将开饭标志flag置为true,但是在编译时,编译器会优化flag变量,在寄存器创建相同的副本。此时,在多线程中,另一个线程启用了开饭标志(将flag置为true),但是寄存器中的副本并未改变,就导致原来地址上的“真”开饭标志flag被忽略掉了。所以,这个开饭标志需要修改为:volatile bool flag = false;

如果变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)


3 - 使用场景

1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;


4 - volatile成员函数

关于类的话,首先如果类是volatile则里面的成员都是volatile的。

其次要将成员函数声明为volatile则同const一样在函数最后声明即可。

当你设计一个类的时候,你声明的那些volatile成员函数是线程安全的,所以那些随时可能被调用的函数应该声明为volatile。

考虑到volatile等于线程安全代码和非临界区;non-volatile等于单线程场景和在临界区之中。我们可以利用这个做一个函数的volatile的重载来在线程安全和速度优先中做一个取舍。


5 - 代码体验

#include <stdio.h>
 
void main()
{
	int i = 10;
	int a = i;
	
	printf("i = %d", a);
 
	// 下面汇编语句的作用就是改变内存中 i 的值
	// 但是又不让编译器知道
	__asm{
		mov dword ptr [ebp-4], 20h
	}
	 
	int b = i;
	printf("i = %d", b);
}

在 Debug 版本模式运行程序,输出结果如下:

i = 10
i = 32

在 Release 版本模式运行程序,输出结果如下:

i = 10
i = 10

输出的结果明显表明,Release 模式下,编译器对代码进行了优化,第二次没有输出正确的 i 值。下面,我们把 i 的声明加上 volatile 关键字,看看有什么变化:

#include <stdio.h>
 
void main()
{
	volatile int i = 10;
	int a = i;
 
	printf("i = %d", a);
	__asm {
		mov dword ptr [ebp-4], 20h
	}
	
	int b = i;
	printf("i = %d", b);
}

分别在 Debug 和 Release 版本运行程序,输出都是:

i = 10
i = 32

这说明这个 volatile 关键字发挥了它的作用。其实不只是“内嵌汇编操纵栈”这种方式属于编译无法识别的变量改变,另外更多的可能是多线程并发访问共享变量时,一个线程改变了变量的值,怎样让改变后的值对其它线程 visible。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值