C++11 标准中引入了内存模型,其目的是为了解决多线程中可见性和顺序(order)。这是c++11最重要的新特征,标准忽略了平台的差异,从语义层面规定了6种内存模型来实现跨平台代码的兼容性。多线程代码因为其本身的复杂性问题,有引入死锁和race condition等一系列问题,可能造成的后果有crash和random error,非常难以debug,因为人的思维是线性的,mutiple threads的代码会交叉运行.

1、同时因为编译器优化和CPU指令集的优化,代码的顺序可能被打乱。那么在多线程环境下。我们看到的结果和我们预期的结果可能不一样。

2、另外多CPU独立Cache Line的同步问题,多核CPU通常有自己的一级缓存和二级缓存,访问缓存的数据很快。但是如果缓存没有同步到主存和其他核心的缓存,其他核心读取缓存就会读到过期的数据。

一个经典的例子如下:

bool flag =false;
int y = ;
// run at thread 1
void M1() {
y = ; //
flag= true; //
}
// run at thread 2
void M2() {
while (!flag); //1
y++; //2

}

如果不考虑reorder,thread2 中y的值肯定是43. 实际上却又两种可能的结果43和1. 为什么会出现这种情况?那就是因为指令重排了。 thread 1中step1 和step2 的顺序可以被编译器或者CPU指令重新reorder。

那么我们就需要武器防止这种reorder的发生。在C+11 之前都是用memory barrier,用于告诉编译器和CPU 哪些地方不能reorder 顺序,同时保证可见性顺序。这就是memory model引入的目前。规定6种不同的order来帮助我们写代码;以上的代码还有race condition的问题,大家看出来了,可能会导致undefined的行为。因为不是原子性的操作,多个线程可能同时操作同一块内存,同样memory model标准应该要杜绝race condition的情况。

如果我们保证不reorder,就一定可以得到正确的结果吗?答案是不能,我们还有考虑多线程之间可见性和顺序性问题。这与重排是不同的概念。

在一个core修改了一个变量,另一个core立马就能读到;或者你修改了两个变量,你要求另一个core在读到这两个变量的时候,要按照相同的顺序,比如这样的代码:

core 1:
x = 1024; flag = true; core 2:
while (!flag) ; assert(x == 1024);

这段代码其实就假定了几件事情,对变量x的修改,要先于对flag的修改;并且在core 2中要感知到这样的顺序。

综上,在多线程中,要保证线程之间的同步问题,需要规定编译器和CPU优化,和CPU cache生效等两大方面问题。C++11 memory model完美解决这两类问题,逻辑层面的东西只能通过语言层面引入model来解决,硬件本身解决不了,编译器无法预知你编码逻辑顺序。
自己期望的代码顺序需要自己保证。工具就是memory model
 

可能有同学会说,有这么复杂吗?我们平常不是直接加mutex互斥锁来保证多线程代码的正确性吗?是的,加锁mutex是通用的办法,加mutex互斥锁的目的是为了多线程之间的顺序性。线程和线程之间通过争取mutex锁,来实现代码正确的order执行顺序。mutex临界区保护的代码区不会跳出临界区的约束。只能等到获取mutex之后才能执行。mutex锁区域内的代码编译器和CPU仍然可以重排。

从可见性角度分析内存模型的order,为了描述多线程之间的代码之间的顺序关系和内存可见性关系。我们用happens before的语义表示,两行代码之间的关系。编译器和CPU的单线程内的优化是按照规则的,优化前的happens before的关系不会被打乱。这也是我们如果是单线程,就无需考虑代码重排的问题。因为编译器和CPU保证语义的正确。我们用happens before来表示我们期待的代码顺序关系。

void func(){
int i=; //1
int j=; //2
int s=i+j; //3
}

1,2 之间没有happen before联系。大家都不依赖对方,所以可以重排。但是3绝对不可能重排到1,2 之前,因为1,2 happens before 3. 这是编译器单线程优化的规则。happen  before不仅仅是顺序,而是可见性。也就是1,2的结果,肯定在3之前就能被step 3感知到。

为什么要强调可见性呢?因子在单线程中,同一个内存read的结果肯定相同,但是在多线程中因为cache,是可能不相同的。那么我们在多线程中,为了表示我们期待的执行结果。我们也用happens before表示。因为执行顺序不代表可见性。

bool flag =false;
int y = ;
// run at thread 1
void M1() {
y = ; //
flag= true; //
}
// run at thread 2
void M2() {
while (!flag); //3
y++; //4 }

多线程之间我们可以认为的规定happens before语义, 如此就能保证正确的结果。那么如何保证 1,2 happens before 3呢,memory model。

既然知道多线程同步的难点,那么看看C++11提供了哪些内存模型:都是基于原子的操作

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;

http://senlinzhan.github.io/2017/12/04/cpp-memory-order/

c++11 standardized memory model 内存模型的更多相关文章

  1. 内存模型 Memory model 内存分布及程序运行中(BSS段、数据段、代码段、堆栈

    C语言中内存分布及程序运行中(BSS段.数据段.代码段.堆栈) - 秦宝艳的个人页面 - 开源中国 https://my.oschina.net/pollybl1255/blog/140323 Mem ...

  2. Cocos2d-x v3.11 中的新内存模型

    Cocso2d-x v3.11 一项重点改进就是 JSB 新内存模型.这篇文章将专门介绍这项改进所带来的新研发体验和一些技术细节. 1. 成果 在 Cocos2d-x v3.11 之前的版本中,使用 ...

  3. java学习:JMM(java memory model)、volatile、synchronized、AtomicXXX理解

    一.JMM(java memory model)内存模型 从网上淘来二张图: 上面这张图说的是,在多核CPU的系统中,每个核CPU自带高速缓存,然后计算机主板上也有一块内存-称为主内(即:内存条).工 ...

  4. Java并发(二):Java内存模型

    一.硬件内存架构 一个现代计算机通常由两个或者多个CPU.其中一些CPU还有多核.每个CPU在某一时刻运行一个线程是没有问题的.如果你的Java程序是多线程的,在你的Java程序中每个CPU上一个线程 ...

  5. 深入理解JMM(Java内存模型) --(七)总结

    JMM 掌管着一个线程对内存的动作 (读和写)影响其他线程对内存的动作的方式.由于使用处理器寄存器和预处理 cache 来提高内存访问速度带来的性能提升,Java 语言规范(JLS)允许一些内存操作并 ...

  6. Java内存模型深度解析:总结--转

    原文地址:http://www.codeceo.com/article/java-memory-7.html 处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会 ...

  7. java内存模型-总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM 和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM 和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照 ...

  8. 深入理解Java内存模型(七)——总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序 ...

  9. 【转】深入理解Java内存模型(七)——总结

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序 ...

随机推荐

  1. SpringBoot学习(五)RSocket和Security

    一.RSocket RSocket是一个用于字节流传输的二进制协议.它通过在单个连接上传递异步消息来支持对称交互模型,主要支持的通讯层包括 TCP, WebSockets和Aeron(UDP). RS ...

  2. LeetCode 958. Check Completeness of a Binary Tree

    原题链接在这里:https://leetcode.com/problems/check-completeness-of-a-binary-tree/ 题目: Given a binary tree, ...

  3. area标签的使用,图片中某一个部分可以点击跳转,太阳系中点击某个行星查看具体信息

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. HTML事件(onclick、onmouseover、onmouseout、this)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. HTTP协议(待写)

    先来了解了解 TCP/IP TCP/IP(Transmission Control Protocol / Internet Protocol)是计算机通讯必须遵守的规则,是不同的通信协议的大集合,其里 ...

  6. python中序列的操作

    Python中的序列操作 可变对象:列表.字典.集合 不可变对象:数值.字符串.元组.forzenset 1.序列的通用操作 (1)测试元素是否存在 x in S和x not in S,返回True或 ...

  7. (尚022)Vue案例_初始化显示(十分详细!!!)

    项目结构目录 所需资料: comment_page文件夹: ====================================================================== ...

  8. (浙江金华)Day 1 组合数计数

    目录 Day 1 组合计数 1.组合数 (1).C(n,m) 读作n选m,二项式系数 : (2).n个东西里选m个的方案数 不关心选的顺序: (3).二项式系数--->多项式系数: 2.组合数计 ...

  9. 干货 | 10分钟掌握branch and cut(分支剪界)算法原理附带C++求解TSP问题代码

    00 前言 branch and cut其实还是和branch and bound脱离不了干系的.所以,在开始本节的学习之前,请大家还是要务必掌握branch and bound算法的原理. 01 应 ...

  10. Xilinx ISE中使用Synplify综合报错的原因

    在Xilinx ISE中使用Synopsys Synplify 综合比较方便,但有时会出现如下错误: "ERROR:NgdBuild: - logical block ' ' with ty ...