C++学习之路(三):volatile关键字
volatile是c++中的一个关键字。用volatile修饰的变量,具有三个性质:易变性
(一)易变性:
由于编译器对代码执行的优化,两条赋值语句,下一条语句可能会直接从上一条语句使用的寄存器中取得变量内容,通过volatile修饰变量,使得变量将被写会内存,对变量的访问也会直接从内存中读取,而不是从寄存器中获取。
测试代码1:
上述代码中,a为非volatile变量,b=a+1,对应的汇编代码为lea ecx, [eax + 1]。由于前一条语句变量a的值被保存在寄存eax中,因此b=a+1,可以直接从寄存器eax中读取值来进行计算。
测试代码2:
变量a为volatile变量,在a=fn(c)被执行后,寄存器ecx中的内容被写回到内存:mov dword ptr [esp+0Ch], ecx,在执行b=a+1时,变量a将直接从内存中进行读取,而不是使用寄存器ecx中的值。
小结:易变性。从汇编层次反应,就是两条语句,下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容,而是直接从内存中读取。
(二)不可优化性
测试代码3:
上述代码由于编译器优化,a,b,c三个变量都是可以通过常量进行替代,最终汇编代码更加简洁。
测试代码4:
由于a,b,c变量都通过volatile修饰,在汇编层面上,编译器将不会进行优化,最终将变量直接从内存读入到寄存器。
小结:不可优化性。volatile告诉编译器,不要对该修饰变量进行优化,保证写在代码中的指令一定会被执行。
(三)顺序性
对于多线程需要被同时访问或修改的变量,为了防止并发访问修改全局变量由于编译器优化带来的问题,常常会加上volatile关键词,来防止编译器进行不必要的优化。
首先给出一个样例代码,之后再来进行讨论:
在上述样例代码中,thread1线程在执行了something赋值后,设置flag为true,然后在thread2线程中判断当前flag为true,表明当前something已经执行完毕,接下来开始执行other things。这里通过if (flag==true)来表示something已经执行完毕,实际情况是不是这样的?先看几个测试代码,再来讨论该场景。
测试代码5:
上述中A,B均为非volatile变量,在函数执行过程中,可以看到通过gcc编译器的优化,两条语句的执行顺序被交换了,即先执行了B=0,再执行了A=B+1,编译器会保证在函数结束之后最终结果没有影响,但实质上可以看出编译器可能会对函数内部指令进行优化。
测试代码6:
这里将一个变量B用volatile进行修饰,但对于编译器来说,实际并没有阻止优化发生,指令依然是乱序执行的。如此:volatile变量,与非volatile变量之间的操作,是可能被编译器优化从而交互顺序执行的。实际上,从这个测试代码也可以看出最初的场景,尽管将flag声明为volatile变量,但由于编译器优化可能导致flag先于something被执行,从而导致切换到thread2线程时,flag判断为true,但并未执行something,从而导致程序逻辑混乱。
测试代码7:
从上述代码中可以看出,当两个变量都被volatile进行修饰时,编译器高度顺序执行了对应指令,并没有进行优化导致乱序执行。如此:volatile变量之间的操作,是不会被编译器交互顺序执行的。
回到最初的那个场景,为了保证something和flag的执行顺序,根据测试代码7,将两个变量都通过volatile,是否可以防止乱序执行的产生?
答案是不行!尽管这样的方法可以避免编译器优化,产生出顺序执行的机器代码,但是最终机器指令是通过CPU来完成执行的,在现代CPU中,本身就已经对指令执行做了很多优化,指令乱序执行就是其提高效率的一种方法,所以将两个变量都声明为volatile,仍然不能保证CPU执行指令时是顺序执行的。
一个正确的做法是:
其实只用保证在thread1中的代码,是先于thread2中的代码执行即可。最为常用的方法,采用互斥锁机制,对执行代码进行加锁,在执行完后进行解锁。
小结:顺序性。volatile变量之间的顺序性,编译器不会进行乱序优化。volatile与非volatile变量之间的顺序,编译器可能会进行优化,不保证顺序。同时,在进行多线程编程时,需要小心使用volatile,最为常用方式是通过互斥锁保证对临界区的访问。
部分内容引用自:https://www.cnblogs.com/god-of-death/p/7852394.html
C++学习之路(三):volatile关键字的更多相关文章
- 学习之路三十九:新手学习 - Windows API
来到了新公司,一开始就要做个程序去获取另外一个程序里的数据,哇,挑战性很大. 经过两周的学习,终于搞定,主要还是对Windows API有了更多的了解. 文中所有的消息常量,API,结构体都整理出来了 ...
- Redis——学习之路三(初识redis config配置)
我们先看看config 默认情况下系统是怎么配置的.在命令行中输入 config get *(如图) 默认情况下有61配置信息,每一个命令占两行,第一行为配置名称信息,第二行为配置的具体信息. ...
- 三 volatile关键字
一:内存模型: 大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问 ...
- Java并发编程学习笔记 深入理解volatile关键字的作用
引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...
- C语言学习笔记--const 和 volatile关键字
1.const关键字 (1)const 修饰的变量是只读的,它不是真正的常量,本质还是变量,只是告诉编译器不能出现在赋值号左边! (2)const 修饰的局部变量在栈上分配空间 (3)const 修饰 ...
- 交互原型设计软件axure rp学习之路(三)
(三)Axure rp元件的触发事件 l OnClick(点击时): 鼠标点击事件,除了动态面板的所有的其他元件的点击时触发.比如点击按钮. l OnMouseEnter(鼠标移入时): 鼠标进入 ...
- zigbee学习之路(三):按键的控制
一.前言 通过前一次的实验,相信大家都已经对cc2530程序的编写有了一定的认识,这次我们来操作和实验的是cc2530上的按键模块. 二.原理分析 我们先来看一下按键的原理图: 根据原理图我们可以得出 ...
- 学习之路三十二:VS调试的简单技巧
这段时间园子里讲了一些关于VS的快捷键以及一些配置技巧,挺好的,大家一起学习,一起进步. 这段时间重点看了一下关于VS调试技巧方面的书,在此记录一下学习的内容吧,主要还是一些比较浅显的知识. 1. 调 ...
- 学习之路三十五:Android和WCF通信 - 大数据压缩后传输
最近一直在优化项目的性能,就在前几天找到了一些资料,终于有方案了,那就是压缩数据. 一丶前端和后端的压缩和解压缩流程 二丶优点和缺点 优点:①字符串的压缩率能够达到70%-80%左右 ②字符串数量更少 ...
- IOS7学习之路三(UISpriteKit游戏开发SKNode)
ios7新添加了自己的游戏开发框架UISpriteKit ,可以用此做一些2D的小游戏, 今天学习了一下SKNode的知识做一下笔记,以便以后查阅. 1.SKNode继承自UIResponder. 2 ...
随机推荐
- 导入导出SQL数据库
在需要导出的数据库名上右键,选择转储SQL-数据和结构 在需要导入的连接中新建相同名称的数据库,右键选择运行SQL文件,即可将数据库数据转储到新的数据库中
- dubbo+zk+maven的那点事
1.服务端建议使用xml方式进行服务暴露,可读性更高: 2.消费端不能直接引入service模块,而是通过引入service-api模块来使用服务端的服务,因为这不是单体应用: 题外话:dubbo是一 ...
- Python文件传输模块ftplib
ftplib是基于FTP协议实现的一个Python模块 from ftplib import FTP # 创建一个FTP连接对象 ftp = FTP() #[ 当带有参数时,即:ftp = FTP(h ...
- 【Codeforces Round #406 (Div. 2)】题解
The Monster 签到题,算一下b+=a和d+=c,然后卡一下次数就可以了. Not Afraid 只要一组出现一对相反数就是安全的. Berzerk 题意:[1,n],两个人轮流走,谁能走到1 ...
- 【以前的空间】bzoj 1052 [HAOI2007]覆盖问题
这道题的思路挺简单的……就是可以证明如果要覆盖一个区域内的点,那么一定有一个正方形在这“区域内的点所围成的最大矩形的四个角中的一个”(不要吐槽很多的“的”……),对于长度r是否可以覆盖整个区域内的点, ...
- async的基本用法
1. async函数的基本形式 //函数声明 async function foo() {} //函数表达式 const foo = async function () {}; //对象的方法 let ...
- 【BZOJ 4555】[Tjoi2016&Heoi2016]求和 多项式求逆/NTT+第二类斯特林数
出处0.0用到第二类斯特林数的性质,做法好像很多,我打的是直接ntt,由第二类斯特林数的容斥公式可以推出,我们可以对于每一个i,来一次ntt求出他与所有j组成的第二类斯特林数的值,这个时候我们是O(n ...
- tinyxml源码解析(上)
转载于:http://www.cnblogs.com/marchtea/archive/2012/11/09/2762669.html 前言: 前段时间做功能可行性的时候简单的使用了tinyxml.在 ...
- mysql 修改max_connections
1.使用命令show variables 来查看当前最大连接数 show variables like '%max_connections%'; 使用命令set global max_connecti ...
- python---异步IO(asyncio)协程
简单了解 在py3中内置了asyncio模块.其编程模型就是一个消息循环. 模块查看: from .base_events import * from .coroutines import * #协程 ...