搜索
当前位置: 秒秒彩官网 > 读入原语 >

volatile的用法与陷阱

gecimao 发表于 2019-05-24 07:28 | 查看: | 回复:

  一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

  如果变量定义的时候用了关键字volatile修饰,但是在其他文件引用时不加volatile变量修饰,同样会被编译器优化掉。反过来想想,原因还是很简单的,MDK编译多个文件时是分别编译,最后再用链接器链接。当编译的时候一个模块引用另外一个模块的变量时,完全是靠的变量声明。如果声明都不加volatile,那么引用的模块肯定会把变量当成普通变量。再反推一下,如果原变量没有加volatile,但是声明的时候加了volatile,是不是引用的模块会将这个变量当成volatile型变量呢?

  C编译器是以每个C文件作为基本编译单元的,称为模块,被编译为obj;而模块之间的函数或变量访问都是通过标号来实现的,标号本身没有任何属性,只是提供给链接器使用的一个符号名称而已,标号的属性完全就靠调用的地方的原型声明来决定的!因此,在一个.C模块中定义为volatile,仅仅是在.C模块中告诉编译器不要优化而已,在另外的模块内使用了这个变量,而它们是不知道该变量是什么属性的,所以只有靠原型声明来告诉编译器这些信息了。

  对于volatile关键字,大部分C语言的教程都是一笔带过,并没有做太深入的分析,所以这里简单的整理了一些关于volatile的使用注意事项。实际上从语法上来看volatile和const是一样的,但是如果const用错,几乎不会有什么问题,而volatile用错,后果可能很严重。所以在volatile的使用上建议大家还是尽量求稳,少用一下没有切实把握的技巧。

  这行代码里reg是指针类型,存储一个指向unsigned char 类型的指针。volatile修饰的是reg这个变量即为指针变量是volatile的,但是指针指向的unsigne char 内容不是volatile。在实际使用的时候编译器对代码中指针变量reg本身的操作不会优化,但是对reg所指的内容 *reg却会作为no-volatile内容处理,对“*reg”的操作还是会被优化。通常这种写法一般用在对共享的指针变量上,即这个指针变量有可能会被中断函数修改,将其定义为volatile后编译器每次取指针变量值的时候都会从内存载入,这样即使这个变量已经被别的程序修改了,当前函数用的时候也能得到修改后的值(否则通常只在函数开始取一次放在寄存器里,以后就一直使用寄存器内的副本)。

  这行代码里volatile修饰的是指针所指的内容,所以这里定义了一个unsigned char类型的指针,并且这个指针指向的是一个volatile的对象,但是指针变量本身不是volatile。如果对指针变量reg本身进行计算或赋值等操作,是可能被编编译器优化的。但是对reg所指的内容*reg的引用却禁止编译器优化。因为这个指针所指的是一个volatile的对象,所以编译器必须保证对*reg的操作都不被优化。通常在驱动程序的开发中,对硬件寄存器指针的定义,都应该采用这种形式。

  这样定义出来的指针本身就是个volatile变量,又指向了volatile的数据内容。

  从字面上看volatile 和 const 似乎是一个对象的两个对立属性,是互斥的。但实际上,两者是有可能一起修饰同一个对象的。看看下面的这行声明:

  这个是在RTOS系统内核中常见的一种声明:rt_clock通常是指系统时钟,他经常被时钟中断进行更新,所以他是volatile的,易变的。因此在用的时候要让编译器每次从内存里面取值。而rt_clock通常只有一个写者(时钟中断),其他地方的使用通常是只读的,所以将其声明为const,表示这里不应该修改这个变量。所以volatile和const是两个不矛盾的东西,并且一个对象同时具备这两种属性也是具有实际意义的。

  注意:上面这个例子里要注意声明和定义是const的使用,在需要读写rt_clock变量的中断函数里应该如下定义

  这里的volatile和const实际上是分别修饰了两个不同的对象:volatile修饰的是指针dvp所指向的类型为struct devregs的数据结构,这个结构对应设备的硬件寄存器,所以是易变的,不能被优化的;而后面的const修饰的是指针变量dvp。应为硬件寄存器地址是一个常量,所以这个指针变量定义成const的不能被修改。

  我们的原意是希望声明一个设备的硬件寄存器组,其中有一个16bit的CSR寄存器,这个寄存器可以由程序向设备写入控制字,也可以由硬件设备设置反应其工作状态。另外还有一个16bit的data数据寄存器,这个寄存器只会有硬件进行设置由程序进行读入。

  看起来这个结构的定义没有什么问题,也符合实际情况。但是如果执行下面这样的代码时,会发生什么情况呢。

  通过一个non-volatile的结构体指针,去访问被定义为volatile的结构体成员,编译器将如何处理?答案是:undefined!C99标准没有对编译器在这种情况下的行为做规定,随意编译器有可能正确的将dvp-car作为volatile的变量来处理,使程序运行正常;也有可能就将dvp-csr作为普通的non-volatile变量来处理,在while当中优化为只有开始的时候取值一次,以后每次循环始终使用第一次取来的值而不再从硬件寄存器里读取,这样上面的代码就有可能死循环。

  如果你使用一个volatile的指针来指向一个非volatile的对象,比如将一个non-volatile的结构体赋给一个volatile的指针,这样对volatile指针所指向结构体的使用都会被编译器认为是volatile的,即便那个对象没有被声明volatile。然而反过来,如果将一个volatile对象的地址赋值给一个non_volatile的普通变量,通过这个指针访问volatile对象的结果是undefined,是危险的。

  这样我们才能保证通过dvp指针去访问结构体成员的时候都是volatile来处理的。

  作者目的也许是希望定义一个volatile的结构体类型,然后顺便定义一个这样的volatile结构体变量,因此定义了一个dev2。然而第二次所定义的dev2变量实际上是non-volatile的!因为实际在定义结构体类型时那个关键字volatile修饰的是dev1这个变量而不是struct devregss类型的结构体,所以这个代码应该写成这样:

  这样的代码常用在对一些DMA设备的发送buffer上。通常这些buffer descriptor(BD)当中的状态会由硬件进行设置已经告诉软件buffer是否完成发送或接收。但是请注意上面的代码中对dvp-ta_bd-state的操作实际上是non-volatile的,这样的操作有可能因为编译器对其读取的优化而导致后面陷入死循环。

  因为虽然dvp已经定义volatile的指针了,但是也只有向其指向的deVregs结构才属于volatile object的范围,也就是说将dvp声明为指向volatile数据的指针可以保障其所指的volatile object之内的tx_bd这个结构体成员自身是volatile变量,但是并不能保证这个指针变量所指的数据也是volatile的(因为这个指针并没有声明为指向volatile的指针)。

  这样可以保障对state成员的处理是volatile的。不过最为稳妥和清晰的办法是这样

  这样在代码里能绝对保障数据结构的易变性,即使数据结构里面没有定义好也没有关系。而且对于日后的维护也有好处:因为这样从代码里一眼就能看出那些数据的访问是必须保证volatile的。

  请问上面标记了1和2的代码哪个是确实在访问volatile对象,而哪个是undefined的结果。

  buffer成员本身是通过一个non-volatile的时钟bdp访问的,按照C99标准的定义,这就属于undefined的情况,因此对bdp-buffer的访问编译器不一定能保证是volatile的;虽然buffer本身可能不是volatile的变量,但是buffer成员是一个指向volatile对象的指针。因此对buffer成员所指向对象的访问编译器可以保证是volatile的,所以bdp-buffer是volatile的。所以,看似简单的volatile的关键字,用起来还是有很多讲究的。

  我在开发中也常常遇到这个问题,发现通常用在两个方面,一方面是对硬件寄存器或固定内存的访问,一般要用到,这就是我们常常在寄存器的头文件常常看到的,另一个就是在多线程,或主程序和中断共享,全局变量常常用到...博文来自:Within的博客

  volatile表示这个变量会被意想不到的改变,既然是意想不到,那怎么知道该给哪个设成volatile?你可以举例状态寄存器里的数(我刚看到的),但这些都是后验的吧?前人吃过亏我们才知道那个是会意想不论坛

  转自[EDN博客精华文章-作者:fafen]在用C语言编程时,经常会遇到使用关键字volatile修饰的变量,比如:在单片机头文件中一些寄存器的定义,如#defineDDRD(*(volatileu...博文来自:zhaoqiaoshi的专栏

  volatile的使用场景volatile用处volatile是java中较轻量的同步原语,用volatile声明的变量保证了该变量的可见性,使用volatile声明的变量被其他线程更改时,该变量会立...博文来自:博客已搬家,请访问新博客提供更好的阅读体验。

  volatile关键字在java多线程中有着比较重要作用,volatile主要作用是可以保持变量在多线程中是实时可见的,是java中提供的最轻量的同步机制。可见性在Java的内存模型中所有的的变量(这...博文来自:nethackatschool的专栏

  1、volatile用于保证多线程并发可见性,其原理在于阻止jvm对volatile语句进行重排序,另外会提供内存屏障(memorybarrier)简单的说,就是当你写一个volatile变量之前,J...博文来自:u011649691的博客

  转发自百度词条:volatile是一个类型修饰符(typespecifier),就像大家更熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量。volatile的作用是作为指令关键字,确...博文来自:yang1393214887的博客

  就像大家更熟悉的const一样,volatile是一个类型修饰符(typespecifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果不加入volatile,基本上会导致这样的结果:要么无...博文来自:予衡飞翔

  volatile是Java提供的一种轻量级的同步机制,同synchronized相比,volatile更轻量级,在访问volatile变量时不会执行加锁操作,因此也就不会使执行的线程阻塞。...博文来自:k393393的博客

  volatile影响编译器编译的结果,volatile指出变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++在产生release版可执行码时会进行编译优...博文来自:Fly

  Java语言提供了一种稍弱的关键词,即Volatile变量,用来确保将变量的更新操作通知到其它线程。仅当Volatile变量能简化代码的实现以及对同步的策略的验证时,才应该使用它们。如果在验证正确性时...博文来自:天空只能仰望吗?

  原文:变量提供了线程的可见性,并不能保证线程安全性和原子性。什么是线程的...博文来自:jinfeiteng2008的专栏

  在大多数情况下,把变量缓存在寄存器中是一个非常有价值的优化方法,如果不用的话很可惜。C和C++给你提供了显式禁用这种缓存优化的机会。如果你声明变量是使用了volatile修饰符,编译器就不会把这个变量...博文来自:小伟

  1、引言volatile影响编译器编译的结果,指出,volatile变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++在产生release版可执行码时会...博文来自:ZP1015

  Java语言包含两种内在的同步机制:同步块(或方法)和volatile变量。这两种机制的提出都是为了实现代码线程的安全性。其中Volatile变量的同步性较差(但有时它更简单并且开销更低),而且其使用...博文来自:Erica_1230的专栏

  找了很多资料,包括《java并发编程实战》,综合一下各家的说法就是:volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。volatile具有synchronized关...博文来自:imzoer的专栏

  volatile有什么含义?有什么用法?官方定义是:一个变量也许会被后台程序改变。关键字volatile与const绝对独立。它指示一个变量也许会被某种方式修改,这种方式按照正常程序流程分析是无法预知...博文来自:joy2017的博客

  一、简介volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized...博文来自:夏日清风

  代码编译环境:Windows732bits+VS2012。volatile是“易变的”、“不稳定”的意思。volatile是C的一个较为少用的关键字,它用来解决变量在“共享”环境下容易出现读取错误的问...博文来自:姜亚轲的博客

  在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值...博文来自:天元喜羊羊的博客

  volatile是Java提供的轻量级的同步机制,比sync的开销要小被volatile定义的变量,系统每次用到它时都是直接从主存中读取,而不是各个线程的工作内存volatile可以像sync一样保持...博文来自:不羁朔风的博客

  1、volatile关键字的作用是什么?多线程中的原子性、可见性、有序性分别表示什么意思?volatile关键字的作用:volatile是java中的一个类型修饰符。它是被设计用来修饰被不同线程访问和...博文来自:的博客

  简述:volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”,可见性的意思是一个线程修改一个共享变量时,另一个线程可以读到这个修改的值,如果volatil...博文来自:summerZBH123的博客

  该篇文章讨论的议题:java语义上的volatile 内存屏障 JVM的实现 生成的汇编指令 如何保障的的可见性和有序性 为什么volatile不能保证复合操作的原子性java语义上的volatile...博文来自:lihbj的博客

  volatile关键字区分C程序员和嵌入式系统程序员的最基本的问题:嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。不懂得volatile内容将会带来灾难...博文来自:liaowo8829的博客

  Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized、volatile、final、concurren包等。本文来分析一下另...博文来自:weixin_38634664的博客

  volatile和synchronized特点首先需要理解线程安全的两个方面:执行控制和内存可见。执行控制的目的是控制代码执行(顺序)及是否可以并发执行。内存可见控制的是线程执行结果在内存中对其它线程...博文来自:Heaven Wang 的专栏

  volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java5之后,volatile关键字才得以重...博文来自:一念永恒

  内存可见性volatile是Java提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色。同synchronized相比(synchronized通常称为重量级锁),volatile...博文来自:m0_37163942的博客

  volatile的特性:  当我们声明共享变量为volatile后,该变量的更新将会对其他线程具有内存可见性,本质就是通过线程间这种数据更新通知来实现数据同步,而传统的做法通过加锁的方式来...博文来自:嘿↗你的益达

  volatile是“易变的”、“不稳定”的意思。volatile是C/C++的一个较为少用的关键字,它用来解决变量在“共享”环境下容易出现的读取错误的问题。...博文来自:Dablelv的博客专栏

  一、volatile关键字多线程比单线程更多的性能消耗,应用得当,则提高效率,提高不当,降低效率;JVM会为每个线程分配一个独立的缓存以此来提高效率如果一个变量被volatile关键字修饰,那么所有线...博文来自:uzong

  volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制。当一个变量被volatile关键字修饰后,它将具备两种特性:  1、保证此变量对所有线、禁止指令重排序先来说说第...博文来自:yanghan1222的博客

  volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储...博文

  硬件问题前面说了一些JMM中的一些基本的概念,为了便于后面的理解,再补充一点别的知识(感觉有个大致印象就够用了)。CPU高速缓存我们都知道,计算机处理一个问题多数情况下,总是需要与内存交互的,这一部分...博文来自:felixus blog

  关于C++中的构造函数、拷贝构造函数、析构函数以及赋值运算符重载的总结博文来自:scmuzi18的博客

  一.前言1.编译器优化介绍:由于内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相...博文来自:冀博

  参数书籍:《Java性能优化权威指南》、《Java并发编程实战》作用:  volatile字段值在所有的线程和CPU缓存中必须保持同步。(参考Java内存模型)  简单讲,你读取的volatile字段...博文来自:zhjali123的专栏

  **JAVA内存模型与JVM内存模型的区别**直接进入正题**JAVA内存模型:Java内存模型规定所有的变量都是存在主存中,每个线程都有自己的工作内存。线程堆变量的操作都必须在工作内存进行,不能直接...博文来自:的博客

  在Java中,每个线程都有自己的堆栈,包括它自己可以访问的变量副本。创建线程时,它会将所有可访问变量的值复制到其自己的堆栈中。该volatile关键字基本上到JVM说:“警告,这个变量可以在另一个线程...博文来自:sinat_23976137的博客

  1、volatile的作用相比Sychronized(重量级锁,对系统性能影响较大),volatile提供了另一种解决可见性和有序性问题的方案。对于原子性,需要强调一点,也是大家容易误解的一点:对vo...博文来自:devotion

  一、Java内存模型想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的。Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工作内存...博文来自:山鬼谣弋痕夕的博客

  Java进阶(二)当我们说线程安全时,到底在说什么 提到线程安全,可能大家的第一反应是要确保接口对共享变量的操作要具体原子性。实际上,在多线程编程中我们需要同时关注可见性、顺序性和原子性问题。本篇文章...博文来自:weixin_28760063的博客

  eclipse调优 一般在不对eclipse进行相关设置的时候,使用eclipse总是会觉得启动好慢,用起来好卡,其实只要对eclipse的相关参数进行一些配置,就会有很大的改善。 加快启动速度 ...博文来自:疯狂小强

  webService学习(二)—— 调用自定义对象参数 本文主要内容: 1、如何通过idea进行webService Client的简单实现(不再使用wsimport的方式,其实是ide帮我们做了...博文来自:止水的专栏

  本篇文章是根据我的上篇博客,给出的改进版,由于时间有限,仅做了一个简单的优化。相关文章:将excel导入数据库2018年4月1日,新增下载地址链接:点击打开源码下载地址十分抱歉,这个链接地址没有在这篇...博文来自:Lynn_Blog

  卷积神经网络是深度学习的基础,但是学习CNN却不是那么简单,虽然网络上关于CNN的相关代码很多,比较经典的是tiny_cnn(C++)、DeepLearnToolbox(Matlab)等等,但通过C语...博文来自:tostq的专栏

  1. 搜索框的属性的设置。要虚拟键盘显示“搜索”二字,需满足以下两个条件: (1)设置input属性 type=‘search’ (2)input需在form表单中。 html代码 ...博文来自:千里马的专栏

  扫二维码关注,获取更多技术分享 本文承接之前发布的博客《 微信支付V3微信公众号支付PHP教程/thinkPHP5公众号支付》必须阅读上篇文章后才可以阅读这篇文章。由于最近一段时间工作比较忙,...博文来自:Marswill

  说明 它一个有jruby语言编写的运行在java虚拟机上的具有收集分析转发数据流功能的工具 能集中处理各种类型的数据 能标准化不通模式和格式的数据 能快速的扩展自定义日志的格式 能非常方便的...博文来自:人生无处不修行

  游戏手柄(JoyStick)编程学习笔记(1)最近我开发的一个项目中需要用手柄控制一个二维移动平台的运动,找了些工业用的操作杆,感觉都挺丑的。后来想到打游戏时用的游戏手柄就挺漂亮,就决定试试用游戏手柄...博文来自:Ivan 的专栏

  深度卷积网络   涉及问题: 1.每个图如何卷积:   (1)一个图如何变成几个?   (2)卷积核如何选择? 2.节点之间如何连接? 3.S2-C3如何进行分配? 4.16-...博文来自:江南研习社

  一、冒泡排序 思想:重复走访过要排序的序列,一次比较两个元素,如果他们的顺序错误就将他们进行交换,一次冒上来的是最小的,其次是第二小。 时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性:稳定 1...博文来自:tan313的专栏

  上周的时候,CESM的环境基本算是搭建完成了,得益于论坛中很多朋友的帮助,希望写一个尽量完整的搭建指南,供新同学参考。 当然欢迎高手们批评指正。 参考帖:博文来自:a1333888的专栏

  在网上所搜索很多操作Word的都是用VC,VS2010做了一些修改,添加操作的方式和用法都有所变化。 要操作Word必须先添加对应的类,如下图在工程中添加操作类(TypeLib中的 MFC类): ...博文来自:xiangjianbo127的专栏

  我们可能经常会用到这一功能,比如有时,我们不希望用户没有进行登录访问后台的操作页面,而且这样的非法访问会让系统极为的不安全,所以我们常常需要进行登录才授权访问其它页面,否则只会出现登录页面,当然我的思...博文来自:沉默的鲨鱼的专栏

  此处仅以VS2010为例,详细说明一下如何在VS环境下生成和使用C++的静态库与动态库。Qt下生成和使用静态和动态库后续再讲。 本文仅供初学者参考,如果有问题欢迎大家指正。        首先简单地理...博文来自:luyan的博客

  强连通分量: 简言之 就是找环(每条边只走一次,两两可达) 孤立的一个点也是一个连通分量   使用tarjan算法 在嵌套的多个环中优先得到最大环( 最小环就是每个孤立点)   定义: int Ti...博文来自:九野的博客

  SQL Server查询和检索操作。 一道例题学会查询和检索操作: 1、在SQL SERVER 2008上附加teaching数据库,其中三张表的含义解释如下: 学生表dbo...博文来自:J.Anson的博客

  jquery/js实现一个网页同时调用多个倒计时(最新的) 最近需要网页添加多个倒计时. 查阅网络,基本上都是千遍一律的不好用. 自己按需写了个.希望对大家有用. 有用请赞一个哦! //js ...博文来自:Websites

  cocos2D-X 3.0渲染结构代码讲解,也是cocos2D-X源码讲解系列文章和从cocos2D-X学习OpenGL系列文章的开始...博文来自:手机游戏开发学习笔记

本文链接:http://k-mood.com/duruyuanyu/365.html
随机为您推荐歌词

联系我们 | 关于我们 | 网友投稿 | 版权声明 | 广告服务 | 站点统计 | 网站地图

版权声明:本站资源均来自互联网,如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

Copyright @ 2012-2013 织梦猫 版权所有  Powered by Dedecms 5.7
渝ICP备10013703号  

回顶部