IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++Atomic与内存序 -> 正文阅读

[C++知识库]C++Atomic与内存序

这篇文章简述C++11之后的内存模型和Atomic中使用的内存序(memory order)。个人作品,禁止转载

参考文献

Memory Models for C/C++ Programmers

相关概念

happens-before

如果语句A和语句B在代码中依次出现,且B的运行依赖A产生的结果,则A必须在B之前执行且B能够看到A的结果,即A happens before B

// example
auto A = 3;
auto B = A * A + 123;
auto C = 10;

则程序的正确运行必须保证A happens before B,但是不需要保证A/B happens before C。

store & load

store即向内存写入,load即从内存读取

synchronized-with

举例说明:考虑2个线程

// thread A
prepare_data();
ready_flag = true;	// <---------------------- point A1
do_other_things();
// thread B
while(ready_flag == false);	// <----------------------- point B1
process_data();

A线程按顺序执行完point A1语句之后,B线程开始执行point B1之后的语句。这种关系是一种同步关系,即B synchronized-with A。这只是一个例子,并非synchronized-with的准确定义

内存模型与内存序

重排

再看这个例子

// thread A
prepare_data();
ready_flag = true;	// <---------------------- point A1
do_other_things();

现代计算机的CPU比内存访问快太多,因此出于性能考虑,要将部分指令重排,以减少内存访问次数。比如编译器或者CPU可能将如上片段重排为

ready_flag = true;	// <---------------------- point A1
prepare_data();
do_other_things();

这使得程序执行错误。因为编译器并不知道ready_flag = true;这句话不能向上重排。因此需要对指令重排进行限制,具体体现为对内存序。

Atomic

C++的原子类型,定义了如下方法

load(std::memory_order order) 
store(T desired, std::memory_order order)
exchange(T desired, std::memory_order order)
compare_exchange_weak(T& expected, T desired, std::memory_order order)
compare_exchange_strong(T& expected, T desired, std::memory_order order)

对整数类型和浮点类型,定义了自加和自减

fetch_add(T arg, std::memory_order order)
fetch_sub(T arg, std::memory_order order)

对整数类型,还定义了原子位运算

fetch_and(T arg, std::memory_order order)
fetch_or(T arg, std::memory_order order)
fetch_xor(T arg, std::memory_order order)

默认情况下,memory_order 为 memory_order_seq_cst,即顺序一致性。比如

atomic<int> i32;
i32++;	// memory_order_seq_cst
i32.fetch_add(2); // memory_order_seq_cst

内存序

C++11定义了如下内存序

memory_order_seq_cst
memory_order_acq_rel
memory_order_release
memory_order_acquire
memory_order_consume
memory_order_relaxed

其中

  • memory_order_acquire,memory_order_consume只能用于读操作
  • memory_order_release只能用于写操作
  • memory_order_acq_rel只能用于读-改-写操作
  • memory_order_relaxed即不需要限制内存序,只需要保证原子性
  • memory_order_seq_cst顺序一致性,即不允许任何重排,默认
    解释如下(如下解释中,load和store都是针对同一个atomic变量)
内存序解释
memory_order_acquire当前load操作之后的所有读、写操作都不能被重排到该load操作上方
memory_order_comsume当前load操作之后的,所有依赖于当前所load的变量的读、写操作都不能被重排到该load操作上方
memory_order_acq_rel本线程中,所有该操作上方的读写操作都不能被重排到该操作下方,该操作下方的读写操作也不能被重排到该操作上方。其它线程中,用release进行的store操作之前的所有写操作,都在该读写改操作之前可见
memory_order_release本线程中该操作之前的所有读写操作都不能被重排到该操作下方
memory_order_seq_cst可以用于store和load,也可以用于读写改。该操作之前的所有读写,不允许被重排到该操作下方;该操作之后的所有读写,不允许被重排到该操作上方

原子量线程同步的例子

如下三种内存序组合,可以用于线程同步

  • memory_order_seq_cst and memory_order_seq_cst
  • memory_order_release and memory_order_acquire
  • memory_order_release and memory_order_consume

memory_order_seq_cst and memory_order_seq_cst

// thread A
c = 0;
x = 0;
a = 1;
b = another_variable;
x.store(1, std::memory_order_seq_cst);     // <----------------Point A
c = 2;
// thread B
cout << c << endl;
while(x.load(std::memory_order_seq_cst) == 1);	// <----------------Point A
cout << a << endl;

线程B的输出,只有可能是0,1。在Point A之前,A线程中a必然已经被赋值,c必然是0。Point A之前的读写操作不会跑到Point A之后,反之亦然

memory_order_release and memory_order_acquire

// thread A
c = 0;
......
b = 1;
x.store(1, std::memory_order_release);     // <----------------Point A
c = 2;
// thread B
while(x.load(std::memory_order_acquire) == 1);    // <----------------Point A
cout << b << c << endl;

线程B的输出,变量b一定是1,因为x.store(1, std::memory_order_release); 之前的读写操作都不能被重排到它下方,线程B的load操作之后的所有读写操作,都不会被重排到它上方。c可能是0或者2,因为memory_order_release并不限制store操作之后的读写操作的重排。

memory_order_release and memory_order_consume

// thread A
c = 0; b = 0;
......
b = 1;
x.store(1, std::memory_order_release);     // <----------------Point A
c = 2;
// thread B
while(x.load(std::memory_order_consume) == 1);    // <----------------Point A
cout << b << c << endl;
cout << x << endl;

线程B的输出,b可能是1和0,因为b可能在x.load之前就读取了;c可能是0或者2,因为它并没有被原子量限制读取顺序,x必然是1,因为x的值和x.load有关,它不能被重排到x.load上方

实践中如何使用

  • 如果没搞清楚,就用默认的memory_order_seq_cst
  • 如果只需要原子性,不需要线程间同步,就用memory_order_relaxed。比如需要多线程共享一个计数器,但是并不需要该计数器绝对准确。

相关概念:内存屏障

之后的C++版本还引入了内存屏障,和内存序相似

  • acquire_memory_fence( void )ensures that all subsequent operations in program order are performed after all preceding loads in program order;
  • release_memory_fence( void )ensures that all preceding operations in program order are performed before all subsequent stores in program order;
  • acq_rel_memory_fence( void ), combines the semantics of acquire and release;
  • ordered_memory_fence( void ), ensures that all preceding operations in program order are performed before all subsequent operations in program order.
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:25:04  更:2022-08-06 10:27:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/27 6:21:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码