c++ volatile关键字

xiaoxiao2021-02-27  154

volatile关键字

  volatile是c/c++中一个鲜为人知的关键字,该关键字告诉编译器不要持有变量的临时拷贝,它可以适用于基础类型  如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者  类的所有成员都会被视为volatile.

  使用volatile并不会否定对CRITICAL_SECTION,Mutex,Event等同步对象的需要  例如:  int i;  i = i + 3;  无论如何,总是会有一小段时间,i会被放在一个寄存器中,因为算术运算只能在寄存器中进行。一般来说,volatitle  关键字适用于行与行之间,而不是放在行内。

  我们先来实现一个简单的函数,来观察一下由编译器产生出来的汇编代码中的不足之处,并观察volatile关键字如何修正  这个不足之处。在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法)

  void getKey(char* pch)  {  while (*pch == 0)  ;  }

  当你在VC开发环境中将最优化选项都关闭之后,编译这个程序,将获得以下结果(汇编代码)

  ;       while (*pch == 0)  $L27  ; Load the address stored in pch  mov eax, DWORD PTR _pch$[ebp]  ; Load the character into the EAX register  movsx eax, BYTE PTR [eax]  ; Compare the value to zero  test eax, eax  ; If not zero, exit loop  jne $L28  ;  jmp $L27  $L28  ;}

  这段没有优化的代码不断的载入适当的地址,载入地址中的内容,测试结果。效率相当的低,但是结果非常准确

  现在我们再来看看将编译器的所有最优化选项开关都打开以后,重新编译程序,生成的汇编代码,和上面的代码  比较一下有什么不同

  ;{   ; Load the address stored in pch  mov eax, DWORD PTR _pch$[esp-4]  ; Load the character into the AL register  movsx al, BYTE PTR [eax]  ; while (*pch == 0)  ; Compare the value in the AL register to zero  test al, al  ; If still zero, try again  je SHORT $L84  ;  ;}

从代码的长度就可以看出来,比没有优化的情况要短的多。需要注意的是编译器把MOV指令放到了循环之外。这在单线程中是一个非常好的优化,但是,在 多线程应用程序中,如果另一个线程改变了变量的值,则循环永远不会结束。被测试的值永远被放在寄存器中,所以该段代码在多线程的情况下,存在一个巨大的 BUG。解决方法是重新  写一次getKey函数,并把参数pch声明为volatile,代码如下:

  void getKey(volatile char* pch)  {  while (*pch == 0)  ;  }

  这次的修改对于非最优化的版本没有任何影响,下面请看最优化后的结果:

  ;{  ; Load the address stored in pch  mov eax, DWORD PTR _pch$[esp-4]  ;       while (*pch == 0)  $L84:  ; Directly compare the value to zero  cmp BYTE PTR [eax], 0  ; If still zero, try again  je SHORT $L84  ;  ;}

  这次的修改结果比较完美,地址不会改变,所以地址声明被移动到循环之外。地址内容是volatile,所以每次循环  之中它不断的被重新检查。

  把一个const volatile变量作为参数传递给函数是合法的。如此的声明意味着函数不能改变变量的值,但是变量的  值却可以被另一个线程在任何时间改变掉。

转载请注明原文地址: https://www.6miu.com/read-10238.html

最新回复(0)