C++ Memory_order的理解

在多核编程中,我们使用锁来避免多个线程修改同一个数据时产生的竞争条件。但是,锁会消耗系统资源,当锁成为性能瓶颈的时候,就需要使用另一种方法——原子指令。c++11中引入了原子类型atomic。

原子指令 (x均为std::atomic) 作用x.load()返回x的值。x.store(n)把x设为n,什么都不返回。x.exchange(n)把x设为n,返回设定之前的值。x.compare_exchange_strong(expected_ref, desired)若x等于expected_ref,则设为desired,返回成功;否则把最新值写入expected_ref,返回失败。x.compare_exchange_weak(expected_ref, desired)相比compare_exchange_strong可能有spurious wakeup。x.fetch_add(n), x.fetch_sub(n)原子地做x += n, x-= n,返回修改之前的值。

但仅靠原子指令实现不了对资源的访问控制。这造成的原因是编译器和cpu实施了重排指令,导致读写顺序会发生变化,只要不存在依赖,代码中后面的指令可能会被放在前面,从而先执行它。cpu这么做是为了尽量塞满每个时钟周期,在单位时间内尽量执行更多的指令,从而提高吞吐率。

// 【内存模型中的同步模式】 memory model synchronization modes
typedef enum memory_order {memory_order_relaxed,    // 【宽松模式】 不对执行顺序做保证memory_order_acquire,    // 【获得模式】本线程中,所有后续的读操作必须在本条原子操作完成后执行memory_order_release,    // 【释放模式】本线程中,所有之前的写操作完成后才能执行本条原子操作memory_order_acq_rel,    // 【获得/释放模式】 同时包含 memory_order_acquire 和 memory_order_releasememory_order_consume,    // 【消费模式】本线程中,所有后续的有关本原子类型的操作,必须在本条原子操作完成之后执行memory_order_seq_cst    // 【顺序一致模式】 sequentially consistent,全部存取都按顺序执行
} memory_order;

下面看个例子:

// thread 1
// ready was initialized to false
p.init();
ready = true;// thread 2
if(ready) {p.bar();
}

线程2在ready为true的时候会访问p,对线程1来说,如果按照正常的执行顺序,那么p先被初始化,然后在将ready赋为true。但对多核的机器而言,情况可能有所变化:

线程1中的ready = true可能会被cpu或编译器重排到p.init()的前面,从而优先执行ready = true这条指令。在线程2中,p.bar()中的一些代码可能被重排到if(ready)之前。

即使没有重排,ready和p的值也会独立地同步到线程2所在核心的cache,线程2仍然可能在看到ready为true时看到未初始化的p。

为了解决这个问题,cpu和编译器提供了memory fence,让用户可以声明访存指令的可见性关系,c++11总结为以下memory order:

memory order

作用

memory_order_relaxed

无fencing作用,cpu和编译器可以重排指令

memory_order_consume

后面依赖此原子变量的访存指令勿重排至此条指令之前

memory_order_acquire

后面访存指令勿重排至此条指令之前

memory_order_release

前面的访存指令勿排到此条指令之后。当此条指令的结果被同步到其他核的cache中时,保证前面的指令也已经被同步

memory_order_acq_rel

acquare + release

memory_order_seq_cst

acq_rel + 所有使用seq_cst的指令有严格的全序关系

有了memoryorder,我们可以这么改上面的例子:

// Thread1
// ready was initialized to false
p.init();
ready.store(true, std::memory_order_release);// Thread2
if (ready.load(std::memory_order_acquire)) {p.bar();
}

线程2中的acquire和线程1的release配对,确保线程2在看到ready==true时能看到线程1 release之前所有的访存操作。

注意,memory fence不等于可见性,即使线程2恰好在线程1在把ready设置为true后读取了ready也不意味着它能看到true,因为同步cache是有延时的。memory fence保证的是可见性的顺序:“假如我看到了a的最新值,那么我一定也得看到b的最新值”。

C++ Memory_order的理解相关推荐

  1. 深入理解C++内存管理

    深入理解C++内存管理 一文了解所有C++内存的问题 AlexCool 目录 一  C++内存模型 二  C++对象内存模型 三 C++程序运行内存空间模型 四  C++栈内存空间模型 五 C++堆内 ...

  2. 深入理解Memory Order

    深入理解Memory Order cpu 保证 cache 编程技术 lock-free wait-free Read–modify–write Compare-And-Swap(CAS) cas原理 ...

  3. 理解 C++ 的 Memory Order 以及 atomic 与并发程序的关系

    为什么需要 Memory Order 如果不使用任何同步机制(例如 mutex 或 atomic),在多线程中读写同一个变量,那么,程序的结果是难以预料的.简单来说,编译器以及 CPU 的一些行为,会 ...

  4. 深入理解C++11pdf

    下载地址:网盘下载 内容简介  · · · · · · <深入理解C++11:C++11新特性解析与应用>内容简介:国内首本全面深入解读C++11新标准的专著,由C++标准委员会代表和IB ...

  5. atomic 内存序_如何理解 C++11 的六种 memory order?

    GTHub:高并发编程--多处理器编程中的一致性问题(下)​zhuanlan.zhihu.com 4 C++ Memory model 4.0 写在前面 C++ memory order是对atomi ...

  6. 通用解题法——回溯算法(理解+练习)

    积累算法经验,积累解题方法--回溯算法,你必须要掌握的解题方法! 什么是回溯算法呢? 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就&quo ...

  7. stream流对象的理解及使用

    我的理解:用stream流式处理数据,将数据用一个一个方法去 . (点,即调用) 得到新的数据结果,可以一步达成. 有多种方式生成 Stream Source: 从 Collection 和数组 Co ...

  8. Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)

    1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...

  9. java局部变量全局变量,实例变量的理解

    java局部变量全局变量,实例变量的理解 局部变量 可以理解为写在方法中的变量. public class Variable {//类变量static String name = "小明&q ...

最新文章

  1. Community Server :: Forums
  2. MapReduce天气案例
  3. windows系统numpy的下载与安装教程
  4. Python Tutorial(六):模块
  5. dp进阶之FFT加速+数据结构优化+不等式优化
  6. ecies算法c语言实现,Bouncy Castle算法库中ECIES算法调用示例
  7. erlang mysql连接超时_Erlang数据库-(一)Erlang与Mysql的连接
  8. OpenGL 法线贴图 切线空间 整理
  9. [Android]OpenGL绘制2D几何图形
  10. 红外避障小车的代码编写
  11. 图片,PDF转换成文字
  12. 天线的回波损耗和驻波比
  13. 失败的过去式英文翻译_过去式用英语怎么说
  14. 0X0000007b
  15. IT战略规划之流程再造
  16. trouble processing xxxx.class: Ill-advised or mistaken usage of a core class (java.* or javax.*)
  17. 银行管理--合规管理(基础概念)
  18. bcdedit编辑启动项 禁用数字签名
  19. java的concurrenthashmap和hashtab
  20. 做开发遇到35岁瓶颈被裁员,体验了一把“自由职业”,最后入行了软件测试...

热门文章

  1. OpenCV adaptiveThreshold 自适应阈值
  2. Halcon 仿射变换
  3. fetch结合(async函数来使用)
  4. Solve error: Cannot open include file: 'X11/Xlocale.h': No such file or directory
  5. 聊聊我对写好程序的认识
  6. java map传入参数_JAVA中map中参数的添加修改
  7. 将一个字段的多个记录值合在一行
  8. java集合框架源代码_面试必备——Java集合框架
  9. 01背包和完全背包 的完整讲解版 包含 一维数组实现 和二维数组实现题目
  10. arp 原理及查杀方式