内存乱序与C++内存模型详解

你也可以通过我的独立博客 —— www.huliujia.com 获取本篇文章

内存乱序

内存乱序指的是内存操作出现乱序,CPU缓存、编译器优化、处理器指令优化等都会改变内存顺序,造成内存乱序。

学习内存顺序容易陷入了一个误区,因为内存顺序是和CPU架构、编译器息息相关的,想要去深入理解CPU缓存怎么导致内存乱序的,编译器优化和处理器指令又是怎么导致内存乱序的,很容易陷入一个又一个填不了的坑。要去了解各种编译器优化技术、了解各种CPU的指令集,甚至连ARM的v7和v8区别都去看了一下。

鲁迅说过,学海无涯,回头是岸。内存顺序或者说内存模型其实是语言层面的东西,所以可以直接从语言层面去理解,所谓内存乱序,在语言层面的表现就是虽然你写了A、B、C三行代码,但是最后在线程中执行顺序可能是CBA,而另外一个线程,观察该线程得到的执行顺序可能是ACB。这些是由于CPU缓存、编译器优化、处理器指令优化等共同造成的。

上述各种优化导致的内存乱序并不是随意地乱序,是有底线的,这个底线就是单线程场景下,优化后程序的执行结果要和优化前保持一致,即让写代码的人感觉是没有优化存在的,代码就是按照他写的顺序执行的。

但是在多线程场景下,内存乱序就会给开发者创造惊喜了,程序的执行结果可能和开发者的预期不一致,看起来像是执行出现问题了,比如下面两个线程:

//线程1
//a,b,c的初始值为0
Thread1()
{
  a = 1; //A  
  b = 2; //B
  c = 3; //C
}

//线程2
Thread2()
{
  printf("a:%d, b:%d, c:%d", a, b,c); 
}

两个线程同时执行,一般来说线程2打印出来的应当是下面四种可能的结果,不应该有其他结果的出现。

  • a:0, b:0, c:0
  • a:1, b:0, c:0
  • a:1, b:2, c:0
  • a:1, b:2, c:3

但是,因为内存乱序的存在,线程1的执行顺序可能是C、B、A。假设线程1观察到的顺序也是C、B、A,那么线程2的输出就可能是(a:0, b:2, c:3),而由于内存乱序也会导致其他线程观察到的执行顺序和线程实际的执行顺序不一致,线程2观察到的顺序可能是A、C、B,那么线程2的输出就是(a:1, b:0, c:2)。

解决上述问题的一个最简单的方式是使用互斥锁,每次访问共享数据之前都加上互斥锁,但是这个很麻烦,意味着每个需要共享的变量都需要在访问前后加上互斥锁。另外一方面,互斥锁很“强”,在一些不需要强一致性的场景下使用互斥锁,就有点杀鸡用牛刀了,会不必要地降低程序性能。

内存屏障(Memory barrier)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值