Datenlord |内存顺序问题(一)

作者 | 王璞

内存顺序,通俗地讲,是关于代码编译成机器指令后的执行顺序问题。内存顺序和编译器、硬件架构密切相关。那为什么会产生内存顺序问题呢?有两方面原因: 一方面,编译器为了优化程序性能,不会完全按照开发者写的代码的顺序来生成机器指令; 另一方面,在程序运行时,为了提高性能,CPU也不完全按照程序的指令顺序执行,比如体系结构里经典的Tomasulo算法。

对于大部分开发者而言,在写单线程程序,或者基于锁(Mutex)和信号量(Semaphore)之类编程框架提供的同步元语写多线程程序的时候,并不需要关心内存顺序的问题。 这是因为编译器和硬件架构保证了,虽然指令执行顺序可能跟开发者写的代码语句的顺序不一致,但是执行后的结果是一样的,即语义一致。 换句话讲,编译器和硬件架构提供了一层抽象用以屏蔽内存顺序问题,保证代码和编译出来的程序执行语义一致。 这样一方面提高程序性能,另一方面让开发者不用关心底层细节。编译器和硬件架构提供的这一层抽象叫作内存模型(Memory Model)。

这种为了便于理解和使用而提出一层抽象以屏蔽底层复杂细节的做法,在各个学科中比比皆是。 类比经典力学和相对论,在远低于光速的运动中,适用经典力学,在接近或达到光速的运动中,适用相对论而不适用经典力学; 经典力学和相对论之间,有一层抽象,速度远低于光速,抽象成立,速度接近或达到光速,抽象被打破。 类似的,编译器和硬件架构提供了内存模型这一层抽象用以屏蔽内存顺序问题。 对于大部分开发者而言,写单线程程序,或基于编程框架提供的同步元语写多线程程序的时候,内存模型抽象成立,无需考虑内存顺序问题; 当开发者写多线程程序,对于多线程并发访问(或读或写)共享数据,使用原子操作,而不是基于锁互斥访问数据,即无锁化编程的时候, 这时内存模型的抽象被打破,开发者必须考虑内存顺序问题。

内存顺序问题涉及编译器和硬件架构的很多细节,我尝试用对于大部分开发者来说浅显易懂的语言来描述内存顺序问题, 尽可能避免编译器和硬件架构的实现细节,以便于大家理解。 下面依次介绍内存模型、内存顺序、原子操作,最后以C++11为例讲解开发者如何规约内存顺序。

内存模型


内存模型是编程语言对程序运行时内存访问模式的抽象,即内存被多个程序(进程和线程)共享,程序对内存的访问是无法预知的。 通俗地讲,内存模型指的是CPU并发随机访问内存,或从内存加载数据(Load)或把数据写入到内存(Store)。 Load和Store是机器指令(或汇编语言)的术语,其实就是读(Read)操作和写(Write)操作。 这里,内存模型屏蔽了很多硬件的细节,比如CPU的寄存器、缓存等等(因为寄存器和缓存属于程序执行上下文,CPU访问寄存器和缓存不存在并发)。 内存模型比较好理解,每个开发者或多或少都接触到内存模型。 有了内存模型这一层抽象,那么内存顺序问题可以等价于读操作和写操作的执行顺序问题,因为内存模型里CPU对内存的访问只有读和写两种操作。

开发者在写代码时,代码语句的先后顺序往往约定了对内存访问的先后顺序的,即使访问的不是同一个内存地址。但是这个约定是基于内存模型这层抽象成立的前提。 前面提到,内存模型在单线程编程和基于编程框架提供的同步元语实现多线程编程的情况下,对内存顺序问题进行屏蔽,怎么理解呢?

下面通过例子说明单线程程序的内存顺序问题:

int x, y = 0;
x = y + 1;
y = 2;

这段代码定义了两个整数,xy,并对y

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达坦科技DatenLord

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值