volatile关键字会阻止编译器去优化“无法被代码改变”的代码。也就是说有些变量非常敏感,以至于编译器认为它不会被修改,但是它却存在被修改的可能。这时就用到了volatile。下面举一些例子来说明
1、线程
void func()
{
int a = 1;
int b= 2;
print(a + b);
a = 3;
}
这个输出会是多少呢?如果两个线程在同时执行func,thread1执行到a=3的时候,thread2刚好执行到b=2,那thread2就应该打印出5. 此时这个a就是非常敏感的,没有volatile时,thread1和thread2都打印3,有volatile就是一个3一个5.这对于公共区域的资源是很关键的,比如卖票。volatile在一定程度上起了互斥锁的作用。
2、iOS(mac os) block内存管理 引用计数
struct Block_byref {
void *isa;
struct Block_byref *forwarding;
volatile int32_t flags; // contains ref count
uint32_t size;
};
我们看到flags变量是由volatile修饰的,注释为包含引用计数。甚至这个flags值在使用的时候,它根本不信任编译器,而是直接强行取地址。
static int32_t latching_incr_int(volatile int32_t *where) {
while (1) {
int32_t old_value = *where;
if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
return BLOCK_REFCOUNT_MASK;
}
if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
return old_value+2;
}
}
}
static bool latching_incr_int_not_deallocating(volatile int32_t *where) {
while (1) {
int32_t old_value = *where;
if (old_value & BLOCK_DEALLOCATING) {
// if deallocating we can't do this
return false;
}
if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
// if latched, we're leaking this block, and we succeed
return true;
}
if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
// otherwise, we must store a new retained value without the deallocating bit set
return true;
}
}
}
可以看到 这两个引用计数加减的函数参数都是volatile int32_t *where
而使用者是latching_incr_int(&src->forwarding->flags);
latching_incr_int(&aBlock->flags);
latching_incr_int_not_deallocating(&aBlock->flags);
使用的是flags的地址,然后这个地址又进行volatile,就是告诉编译器,flags不用你优化,连存放flags的地址,你也不需要优化!
3、看汇编
以x86汇编为例了,该样例摘自维基百科
- 首先是不加volatile
#include <stdio.h>
int main() {
int a = 10, b = 100, c = 0, d = 0;
printf("%d", a + b);
a = b;
c = b;
d = b;
printf("%d", c + d);
return 0;
}
gcc -O3 -S without.c -o without.s
.file "without.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.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 $20, %esp
movl $110, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $200, 4(%esp)
movl $.LC0, (%esp)
call printf
addl $20, %esp
xorl %eax, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.1 20070719 [FreeBSD]"
- 加volatile
#include <stdio.h>
int main() {
volatile int a = 10, b = 100, c = 0, d = 0;
printf("%d", a + b);
a = b;
c = b;
d = b;
printf("%d", c + d);
return 0;
}
gcc -S with.c -o with.s
.file "with.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.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
movl $10, -8(%ebp)
movl $100, -12(%ebp)
movl $0, -16(%ebp)
movl $0, -20(%ebp)
movl -8(%ebp), %edx
movl -12(%ebp), %eax
movl $.LC0, (%esp)
addl %edx, %eax
movl %eax, 4(%esp)
call printf
movl -12(%ebp), %eax
movl %eax, -8(%ebp)
movl -12(%ebp), %eax
movl %eax, -16(%ebp)
movl -12(%ebp), %eax
movl %eax, -20(%ebp)
movl -16(%ebp), %edx
movl -20(%ebp), %eax
movl $.LC0, (%esp)
addl %edx, %eax
movl %eax, 4(%esp)
call printf
addl $36, %esp
xorl %eax, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.1 20070719 [FreeBSD]"
网友评论