多线程的好伙伴 voliate

Last updated on 8 months ago

voliate 经常看源码时看到,以前在校时看八股文也看到过… 但看了总是忘记,这里做个记录..

原理

我们都知道 读寄存器值比读内存快很多,编译器为了加速访问,会对一些代码中的变量进行优化,例如将其缓存到寄存器中,以减少对内存的访问次数,或者在适当的情况下,将其合并或优化掉。

通俗的说,这个变量在内存更新了(可能是被其他线程写更新了),但是寄存器没更新,导致读这个变量还是原来的值,从而出现了预期之外的代码逻辑;

对于单线程来说,这个优化肯定好的,是多线程的场景下,可能就有问题,可能程序跑了很久没出现问题,但可能未来的某一刻出现这种问题,就很棘手,也很难定位;

举个简单场景:

  • 多线程场景,一个线程执行 check_flag 函数循环检测 falg 变量,为1 则执行 业务代码,0不执行; 另一个线程执行 modify_flag 函数,不断的更新 flag 变量;

    有一个场景就是 , falg 原本为1 ,modify_flag更新0;此时 int value = flag; 获取的还是原本的为1;导致了 出现另一种逻辑; flag为0 的情况下,会执行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int flag;
void* check_flag(void* arg) {
while (1) {
int value = flag;
if(value){
/**do 业务代码**/
}
}
return NULL;
}

void* modify_flag(void* arg) {
while (1) {
flag = 1;
usleep(10000); // sleep for 1 second
flag = 0;
usleep(10000); // sleep for 1 second
}
return NULL;
}

如何防止这种优化?

这种优化是很好的,提高访问速度,并减少对内存的访问次数,也不能把这个优化特性全去掉,于是为为了人性化操作,对于不想参与这种优化的变量,在这变量之前加入关键字 volatile,编译则不会进行优化,不改变程序行为的前提下重新排列代码或者使用寄存器来存储变量的值,确保对变量的访问是实时的。

注意事项

volatile 关键字只是一种提示,它告诉编译器不要对被声明为 volatile 的变量进行优化。它并不提供线程安全性或同步,也不能解决所有并发问题。 必要时还是需要采取同步措施。