内存顺序
内存顺序描述了计算机 CPU 获取内存的顺序,内存的排序既可能发生在编译器编译期间,也可能发生在 CPU 指令执行期间。
为了尽可能地提高计算机资源利用率和性能,编译器会对代码进行重新排序, CPU 会对指令进行重新排序、延缓执行、各种缓存等等,以达到更好的执行效果。当然任何排序都不能违背代码本身所表达的意义,并且在单线程情况下通常不会有任何问题。
但是在多线程环境下,比如无锁(lock-free)数据结构的设计中,指令的乱序执行会造成无法预测的行为,所以我们通常引入内存栅栏(Memory Barrier)这一概念来解决可能存在的并发问题。
Memory Barrier
内存栅栏是一个令 CPU 或编译器在内存操作上限制内存操作顺序的指令,通常意味着在 barrier 之前的指令一定在 barrier 之后的指令之前执行。
在 c11和c++11 中,引入了六种不同的 memory order,可以让程序员在并发编程中根据自己需求尽可能降低同步的粒度,以获得更好的程序性能。这六种 order 分别是:
relaxed、acquire、release、consume、acq_rel、seq_cst
memory_order_relaxed: 只保证当前操作的原子性,不考虑线程间的同步,其他线程可能读到新值,也可能读到旧值。
memory_order_release: 对写入施加 release 语义(store),在代码中这条语句前面的所有读写操作都无法被重排到这个操作之后。
memory_order_acquire: 对读取施加 acquire 语义(load),在代码中这条语句后面所有读写操作都无法重排到这个操作之前。
memory_order_consume:对当前要读取的内存施加 release 语义(store),在代码中这条语句后面所有与这块内存有关的读写操作都无法被重排到这个操作之前。
memory_order_acq_rel: 对读取和写入施加 acquire-release 语义,无法被重排可以看见其他线程施加 release 语义的所有写入,同时自己的 release 结束后所有写入对其他施加 acquire 语义的线程可见。
memory_order_seq_cst: 顺序一致性,如果是读取就是 acquire 语义,如果是写入就是 release 语义,如果是读取+写入就是 acquire-release 语义同时会对所有使用此 memory order 的原子操作进行同步,所有线程看到的内存操作的顺序都是一样的,就像单个线程在执行所有线程的指令一样。
通常情况下,默认使用 memory_order_seq_cst,所以如果不确定怎么这些 memory order,就用这个。