最近学习多线程编程的时候,测试加锁不加锁的时候,碰到了一个不能理解的现象,通过搜索,发现多线程编程的坑很深,需要考虑很多方面的情况,如编译器优化,内存模型,cpu架构。
现象:没有使用锁的情况下,两个线程访问同一个全局变量,一个线程修改了该全局变量后,在另一个线程中始终无法发现修改的值。
大致的代码描述:
int a = 0;
void * threadFun1(void *)
{
a = 1;
}
void * threadFun2(void *)
{
while (1)
{
if (a == 1)
{
//do something
}
}
}
线程函数threadFun2
始终没有完成do something
的任务,原因是获取到a
的值始终是0,并没有刷新为由线程函数threadFun1
修改的值1。
本现象的原因是编译器优化造成的,线程函数threadFun2
中由于频繁的访问a
,a
的值用的是本线程缓存的值,因此不能看到主存中a
的新值。测试下面两种方法有效:
- 设置编译器不优化。cmake中加入
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -00")
- 使用volatile关键字,强制每次从内存读取或刷新变量的值。
volatile int a = 0
这种现象就是共享变量的可见性问题,上面的两种方法都不是解决问题的真正方法,真正的方法是加锁,保证每次只有一个线程能访问共享的变量,并且刷新和读取最新的内存值。
参考连接:
https://www.cnblogs.com/soaringEveryday/p/4418604.html
https://blog.youkuaiyun.com/u012887385/article/details/57984439
https://www.codenong.com/18599012/
https://www.it-swarm.dev/zh/c++/什么时候使用volatile多线程?/970260499/
https://changkun.de/modern-cpp/zh-cn/07-thread/index.html