C++中的内存序std::memory_orde_*
摘自:https://cloud.tencent.com/developer/article/2026043
1) C++11中引入了六种内存约束符用以解决多线程下的内存一致性问题(在头文件<atomic>中),其定义如下:
typedef enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order;
1) 这六种内存约束符从读/写的角度进行划分的话,可以分为以下三种:
- 读操作(memory_order_acquire memory_order_consume)
- 写操作(memory_order_release)
- 读-修改-写操作(memory_order_acq_rel memory_order_seq_cst)
ps: 因为memory_order_relaxed没有定义同步和排序约束,所以它不适合这个分类。
2) 从访问控制的角度可以分为以下三种:
- Sequential consistency模型(memory_order_seq_cst)
- Relax模型(memory_order_relaxed)
- Acquire-Release模型(memory_order_consume memory_order_acquire memory_order_release memory_order_acq_rel)
从访问控制的强弱排序,Sequential consistency模型最强,Acquire-Release模型次之,Relax模型最弱。
3) C++11提供的6种内存访问约束符中:
- memory_order_release:在当前线程T1中,该操作X之前的任何读写操作指令都不能放在操作X之后。如果其它线程对同一变量使用了memory_order_acquire或者memory_order_consume约束符,则当前线程写操作之前的任何读写操作都对其它线程可见(注意consume的话是依赖关系可见)
- memory_order_acquire:在当前线程中,load操作之后的依赖于此原子变量的读和写操作都不能被重排到当前指令前。如果有其他线程使用memory_order_release内存模型对此原子变量进行store操作,在当前线程中是可见的。
- memory_order_relaxed:没有同步或顺序制约,仅对此操作要求原子性
- memory_order_consume:在当前线程中,load操作之后的依赖于此原子变量的读和写操作都不能被重排到当前指令前。如果有其他线程使用memory_order_release内存模型对此原子变量进行store操作,在当前线程中是可见的。
- memory_order_acq_rel:等同于对原子变量同时使用memory_order_release和memory_order_acquire约束符
- memory_order_seq_cst:从宏观角度看,线程的执行顺序与代码顺序严格一致
C++的内存模型则是依赖上面六种内存约束符来实现的:
- Relax模型:对应的是memory_order中的memory_order_relaxed。从其字面意思就能看出,其对于内存序的限制最小,也就是说这种方式只能保证当前的数据访问是原子操作(不会被其他线程的操作打断),但是对内存访问顺序没有任何约束,也就是说对不同的数据的读写可能会被重新排序
- Acquire-Release模型:对应的memory_order_consume memory_order_acquire memory_order_release memory_order_acq_rel约束符(需要互相配合使用);对于一个原子变量A,对A的写操作(Release)和读操作(Acquire)之间进行同步,并建立排序约束关系,即对于写操作(release)X,在写操作X之前的所有读写指令都不能放到写操作X之后;对于读操作(acquire)Y,在读操作Y之后的所有读写指令都不能放到读操作Y之前。
- Sequential consistency模型:对应的memory_order_seq_cst约束符;程序的执行顺序与代码顺序严格一致,也就是说,在顺序一致性模型中,不存在指令乱序。
4) 下面这幅图大致梳理了内存模型的核心概念