美文网首页
代码讲解 c 语言 volatile 关键字的作用

代码讲解 c 语言 volatile 关键字的作用

作者: sarto | 来源:发表于2022-09-21 15:18 被阅读0次

先抛出结论:c 语言 volatile 关键字的作用在于提示编译器,这个变量值可能被其他修改,在取值时要从内存读取。

后续会解释,从内存中读取是什么意思。

构造这样一种情况,在多线程环境下,有一个全局变量 i 初始值为 0,线程 A 轮寻 i 的值,当 i 的值为 1 时退出;线程 B 在某一时刻改变 i 的值,然后退出。我们期望无论 A B 线程执行顺序如何,线程 A B 都可以正确的退出。

测试代码

1   #include<stdlib.h>                                                                                                                                                                        
  1 #include<pthread.h>
  2 
  3 int i=0;
  4 
  5 void* threadA() {
  6   for(;!i;) {}
  7 }
  8 
  9 void* threadB() {
 10   i=1;
 11 }
 12 
 13 
 14 int main() {
 15   pthread_t a;
 16   pthread_t b;
 17 
 18   pthread_create(&a, NULL, threadA, NULL);
 19   pthread_create(&b, NULL, threadB, NULL);
 20 
 21 
 22   void* ret;
 23   pthread_join(a, &ret);
 24   pthread_join(b, &ret);
 25 }

如果将这段代码编译运行,程序不会退出。

merore@merore-pc:/wk/wk/codebook/volatile/c$ ./no-volatile.out 

加上 volatile

1   #include<stdlib.h>                                                                                                                                                                        
  1 #include<pthread.h>
  2 
  3 volatile int i=0;
  4 
  5 void* threadA() {
  6   for(;!i;) {}
  7 }
  8 
  9 void* threadB() {
 10   i=1;
 11 }
 12 
 13 
 14 int main() {
 15   pthread_t a;
 16   pthread_t b;
 17 
 18   pthread_create(&a, NULL, threadA, NULL);
 19   pthread_create(&b, NULL, threadB, NULL);
 20 
 21 
 22   void* ret;
 23   pthread_join(a, &ret);
 24   pthread_join(b, &ret);
 25 }

程序正常退出

merore@merore-pc:/wk/wk/codebook/volatile/c$ ./volatile.out 
merore@merore-pc:/wk/wk/codebook/volatile/c$ 

那么 volatile 究竟起一个什么效果呢?将这两段代码分别编译成汇编文件

merore@merore-pc:/wk/wk/codebook/volatile/c$ make
gcc -Os -S -o no-volatile.s no-volatile.c
gcc -Os -S -o volatile.s volatile.c

带 volatile

8: 线程 A 加载变量 i 的地址到 r13 寄存器
10: 读取 r13 的值所指向的内存,即从内存中读取 i 的值
11: 如果值为 0 ,跳到 .L2,然后再次读取循环,否则继续执行 12 行,退出线程,这里 jr $r1 相当于 c 语言 return。

可以看到,这个程序逻辑和我们期望的是一致的,一旦 i 的值发生改变,总可以读到 i 的新值然后退出线程。

  5 threadA:
  6 .LFB6 = .
  7   .cfi_startproc
  8   la.local  $r13,i
  9 .L2:
 10   ldptr.w $r12,$r13,0
 11   beqz  $r12,.L2
 12   jr  $r1
 13   .cfi_endproc
 14 .LFE6:
 15   .size threadA, .-threadA
 16   .align  2
 17   .globl  threadB
 18   .type threadB, @function

不带 volatile

8: 加载变量 i 的地址到 r12
9: 从内存读取变量 i 的值到 r12
10: 如果 r12 不等于 0,则跳到 .L5 即退出线程,否则,继续走到 .L4。一旦程序走到 12 行,将陷入循环。不会再次读取值做比较。

可以看到,在不带 volatile 的情况下,程序行为和我们预期的不一致,程序无法正常退出。

  5 threadA:
  6 .LFB6 = .
  7   .cfi_startproc
  8   la.local  $r12,i
  9   ldptr.w $r12,$r12,0
 10   bnez  $r12,.L5
 11 .L4:
 12   b .L4
 13 .L5:
 14   jr  $r1
 15   .cfi_endproc
 16 .LFE6:
 17   .size threadA, .-threadA
 18   .align  2
 19   .globl  threadB
 20   .type threadB, @function

总结

这里就明白了,要从内存中读取被 volatile 修饰的变量是什么意思。总之,在并发环境下,共享变量都应该加上 volatile 修饰。否则会因为 gcc 的一些编译优化行为造成语义不一致。

相关文章

  • C++中volitale与java中Valitale的区别

    C语言中volatile关键字的作用 volatile的本意是“易变的” 因为访问寄存器要比访问内存单元快的多,所...

  • volatile关键字

    关键字volatile的作用是使变量在多个线程间可见。 在讲解volatile关键字之前我们先来看下下面这个例子:...

  • C语言之内存讲解

    1 内存讲解 1.1 作用域 C语言变量的作用域分为:代码块、函数、文件作用域: 1.1.1 局部变量 局部变量(...

  • java volatile关键字作用及使用场景

    volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果...

  • C/C++ 中volatile关键字

    以下所描述的volatile关键字仅仅针对C/C++语言中的,并不适用于其他语言. volitate 一个定义为v...

  • volatile关键字

    java中关键字volatile的作用; volatile vs synchronized的区别 Java并发编...

  • 面向对象基本语法

    OC完全兼容C(.m)语言,可以在oc语言中混入c(.c)语言代码甚至是c++(.mm)代码。 oc关键字:基本上...

  • C语言关键字volatile

    作用防止编译器对可能以编译器无法确定的方式更改的对象应用任何优化volatile int *p = /* ... ...

  • Java 并发编程—volatile 关键字

    volatile 关键字 volatile 作用 volatie 用于修饰变量,它有两个作用。 它可以保证变量在多...

  • 多线程下volatile 关键字的作用

    Volatile 关键字 作用:Volatile 关键字是多线程下,最小轻量级的同步机制。过程:首先Volatil...

网友评论

      本文标题:代码讲解 c 语言 volatile 关键字的作用

      本文链接:https://www.haomeiwen.com/subject/aofiortx.html