volatile在多线程中的应用

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

有这样一个例子:  

#include   
#include   
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))   
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.eechina.com/thread-20227-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表