volatile在多线程中的应用

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编译器就不会优化了,每次都被迫去内存中去取值,达到了我们预期的结果。
————————————————
版权声明:本文为CSDN博主「farsightliuht」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/farsightliuht/article/details/6521813

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值