C/C++ 的volatile
volatile是在C,C++,Java等中语言中的一种修饰关键字。
这个关键字在嵌入式系统中,是一个非常重要的一个使用。尽管在一般的Application中,可能很多人都不需要使用这个。但是在单片机中,如果不熟悉这个关键字,很有可能产生想像不到的意外。
那么,我就来谈谈Volatile的意义--volatile在ANSIC(C89)以后的C标准规格和const一起被包含在内。在标准C中,这些关键字是必定存在的。
关于volatile的意义,根据标准C的定义、
volatile的目的是,避免进行默认的优化处理.比如说对于编译器优化的功能,如果从编译器看来,有些多余的代码的话,编译器就会启动优化程序,并删除一些代码,但是这在嵌入式系统中很有可能是关键性的处理,必须不能保证被编译器删掉,所以提供了Volitile来声明,告诉编译器无论如何都不要删掉我
C/C++中的volatile使用時機?
.不知各位對volatile(揮發性的)這個字陌不陌生? 我相信大家在一些程式或多或少都看
過這個字眼, 但是究竟要在何種場合用它呢?
.當然一定是有需要, C/C++才會有這個保留字, 否則只是增加programmer的困擾而已
.有2兩個場合(I/O & multithread program), 供各位參考!
.請大家check自己的程式中(尤其是第2個場合), 若有的話請記得加上volatile
1. I/O, 假設有一程式片斷如下
U8 *pPort;
U8 i, j, k;
pPort = (U8 *)0x800000;
i = *pPort;
j = *pPort;
k = *pPort;
以上的i, j, k很有可能被compiler最佳化而導致產生
i = j = k = *pPort;
的code, 也就是說只從pPort讀取一次, 而產生 i = j = k 的結果, 但是原本的程式的目
的是要從同一個I/O port讀取3次的值給不同的變數, i, j, k的值很可能不同(例如從此
I/O port 讀取溫度), 因此i = j = k的結果不是我們所要的
怎麼辦 => 用volatile, 將
U8 *pPort;
改為
volatile U8 *pPort;
告訴compiler, pPort變數具有揮發性的特性, 所以與它有關的程式碼請不要作最佳化動作. 因而
i = *pPort;
j = *pPort;
k = *pPort;
此三列程式所產生的code, 會真正地從pPort讀取三次, 從而產生正確的結果
2. Global variables in Multithread program
=> 這是在撰寫multithread program時最容易被忽略的一部份
=> 此原因所造成的bug通常相當難解決(因為不穩定)
假設有以下程式片斷, thread 1 & thread 2共用一個global var: gData
thread 1: thread 2:
... ....
int gData; extern int gData;
while (1) int i, j, k;
{
.... for (i = 0; i < 1000; i++)
gData = rand(); {
..... /* A */
} j = gData;
....
.... }
在thread 2的for loop中, 聰明的compiler看到gData的值, 每次都重新從memory load到register,
實在沒效率, 因此會產生如下的code(注意,tmp也可以更進一步的用register取代):
tmp = gData;
for (i = 0; i < 1000; i++
{
/* A */
j = tmp;
....
}
也就是gData只讀取一次, 這下子問題來了, 說明如下:
.thread 2在執行for loop到j = gData的前一列(A)的時候(假設此時gData=tmp=5), 被切換到thread 1執行
.在thread 1的while loop中透過gData = rand(), 對gData做了修改(假設改為1), 再切換回thread 2執行
.繼續執行 j = gData, 產生j = 5的結果
.但是正確的結果應該是 j = 1
怎麼辦 => 也是用volatile,
在thread 1中, 將
int gData;
改為
volatile int gData;
在thread 2中, 將
extern int gData;
改為
extern volatile int gData;
- 实例分析
-
1. 在函数A中起了一个线程

2. 在函数B中将全局变量g_bStartProcess置为Ture,并同时打印出了该全局变量的值,如下图;g_bStartProcess声明为static bool g_bStartProcess = false;

3.线程函数实现大体如下图:

开机后会先调用函数A,之后调用函数B,而且在log里有看到g_bStartProcess的值确实为true,但是线程函数while(g_bStartProcess)循环就是进不去,如果我把while(g_bStartProcess)上面的ALOGV打开后,while(g_bStartProcess)竟然神奇的能进去,而如果把ALOGV关掉后,就又进不去。这就是编译器优化导致的。将g_bStartProcess的声明改为static volatile bool g_bStartProcess = false;改了后while(g_bStartProcess)就能进去了,至此问题解决了
-
解释下这里编译器优化的原因:
- 全局变量g_bStartProcess 是memory中的
- Process A 用g_bStartProcess 时,会从memory load 到 register 中
- 因为在 Process A 使用时,编译器自作聪明,发现register 已经load了,没有重新load 一次。但此时 Process B 已经把这个值修改了。所以发生了错误。
-
避免这种情况的方法:
- 互斥锁等
- Thread 中用局部变量去读取全局变量,改变编译器的优化行为
- Volatile: 只适用于单个变量。
volatile是C/C++中用于告诉编译器不要对特定变量进行优化,尤其在嵌入式系统和多线程编程中防止意外的代码优化。在I/O操作和多线程共享全局变量时,使用volatile可以确保每次访问变量时都从内存读取,避免编译器的优化导致的问题。文章通过实例解释了volatile在避免编译器优化和解决多线程同步问题中的作用。
1360

被折叠的 条评论
为什么被折叠?



