原创 volatile在多线程中的应用

2009-7-9 12:04 2097 3 3 分类: MCU/ 嵌入式

 


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


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编译器就不会优化了,每次都被迫去内存中去取值,达到了我们预期的结果。

文章评论0条评论)

登录后参与讨论
我要评论
0
3
关闭 站长推荐上一条 /2 下一条