volatile在多线程中的应用

时间:2009-06-15

  作者:曹忠明,华清远见嵌入式学院讲师。

  volatile在词典中的意思是易变的,反复无常的。它在我们的程序设计中常常用到的。volatile是一个关键字,用来修饰一个变量,告诉编译器在编译的时候不要对其进行优化,在操作寄存器和多线程中这种用法是常见的。

  有这样一个例子:

  #include <stdio.h>

  #include <pthread.h>

  void my_func();

  int? i;

  int main()

  {

  pthread_t my_thread;

  int err,k;

  if ((err = pthread_create(&my_thread,NULL,(void *)my_func,NULL)) < 0)

  perror("can't create thread:%s\n");

  i = 2;

  while(i == 2);

  printf("main:%d\n",i);

  while(1);

  return 0;?

  }

  void my_func()

  {

  sleep(1);

  i = 3;

  printf("my_func:%d\n",i);

  }

  这个例子本意是想让主程序进入while(i == 2)这个循环,直到线程中将这变量i的值修改后跳出循环,可是结果是

  my_func:3

  这与想像中的结果完全不一样,是什么原因造成这样的结果呢?查看一下汇编代码,才知道,是编译器将这段代码给优化掉了,汇编代码如下:

  .file   "test.c"

  .section      .rodata.str1.1,"aMS",@progbits,1

  .LC0:

  .string           "my_func:%d\n"

  .text

  .p2align 4,,15

  .globl my_func

  .type      my_func, @function

  my_func:

  pushl      %ebp

  movl      %esp, %ebp

  subl $8, %esp

  movl      $1, (%esp)

  call     sleep

  movl      $3, 4(%esp)

  movl      $.LC0, (%esp)

  movl     $3, i

  call    printf

  leave

  ret

  .size my_func, .-my_func

  .section      .rodata.str1.1

  .LC1:

  .string      "can't create thread:%s\n"

  .text

  .p2align 4,,15

  .globl main

  .type      main, @function

  main:

  leal      4(%esp), %ecx

  andl $-16, %esp

  pushl      -4(%ecx)

  pushl      %ebp

  movl      %esp, %ebp

  pushl      %ecx

  subl $36, %esp

  leal      -8(%ebp), %eax

  movl      $0, 12(%esp)

  movl      $my_func, 8(%esp)

  movl      $0, 4(%esp)

  movl      %eax, (%esp)

  call    pthread_create

  testl %eax, %eax

  js      .L9

  .L4:

  movl      $2, i

  .L6:

  jmp    .L6

  .L9:

  movl      $.LC1, (%esp)

  call    perror

  jmp    .L4

  .size main, .-main

  .comm      i,4,4

  .ident      "GCC: (GNU) 4.1.3 20080623 (prerelease) (Ubuntu 4.1.2-23ubuntu3)"

  .section      .note.GNU-stack,"",@progbits

  在定义变量i的时候添加上volatile后:

  int volatile i;

  的结果为:

  my_func:3

  main:3

  这个结果显然达到了我们预期的效果,再查看一下他的汇编代码,会看到那个带有条件的循环语句。

  .file      "test.c"

  .section      .rodata.str1.1,"aMS",@progbits,1

  .LC0:

  .string      "my_func:%d\n"

  .text

  .p2align 4,,15

  .globl my_func

  .type      my_func, @function

  my_func:

  pushl      %ebp

  movl      %esp, %ebp

  subl $8, %esp

  movl      $1, (%esp)

  call   sleep

  movl     $3, i

  movl      i, %eax

  movl      $.LC0, (%esp)

  movl      %eax, 4(%esp)

  call  printf

  leave

  ret

  .size my_func, .-my_func

  .section      .rodata.str1.1

  .LC1:

  .string      "can't create thread:%s\n"

  .LC2:

  .string      "main:%d\n"

  .text

  .p2align 4,,15

  .globl main

  .type      main, @function

  main:

  leal      4(%esp), %ecx

  andl $-16, %esp

  pushl      -4(%ecx)

  pushl      %ebp

  movl      %esp, %ebp

  pushl      %ecx

  subl $36, %esp

  leal      -8(%ebp), %eax

  movl      $0, 12(%esp)

  movl      $my_func, 8(%esp)

  movl      $0, 4(%esp)

  movl      %eax, (%esp)

  call    pthread_create

  testl %eax, %eax

  js      .L13

  .L4:

  movl      $2, i

  .L6:

  movl      i, %eax

  cmpl      $2, %eax

  je     .L6

  mov      i, %eax

  movl      $.LC2, (%esp)

  movl      %eax, 4(%esp)

  call    printf

  .L8:

  jmp   .L8

  .L13:

  movl      $.LC1, (%esp)

  call   perror

  .p2align 4,,3

  jmp   .L4

  .size main, .-main

  .comm      i,4,4

  .ident      "GCC: (GNU) 4.1.3 20080623 (prerelease) (Ubuntu 4.1.2-23ubuntu3)"

  .section      .note.GNU-stack,"",@progbits

  比较红色部分就会看到是什么造成这种差异了!

  为什么加上volatile和不加就有这么大的差距的,原因是每次使用变量都去内存中取值,然后通过系统总线传到CPU处理,会增加很大的开销,所以在CPU的cache中位变量啊做了一个副本,通过这个副本来进行赋值。在程序中首先对i 进行复制“i = 2”,然后又将i和2进行比较“i == 2”编译器认为i的值是2,没有变化,认为这个比较没有意义就将其优化掉了,将程序陷入无条件的死循环中,在线程my_func中修改i 的值夜就没有意义了,终结果就是我么看到了那样了,加上volatile编译器就不会优化了,每次都被迫去内存中去取值,达到了我们预期的结果。

  “本文由华清远见https://www.embedu.org/index.htm提供”



  
上一篇:基于ISO 11784/5的动物识别标签设计
下一篇:基于FPGA平均值原理相位差计的设计

免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

相关技术资料