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. Apache Shiro<=1.2.4反序列化RCE漏洞

    介绍:Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 漏洞原因:因为shiro对cookie里的rememberme字段进行了反序列化,所以如果知道了 ...

  2. Bagging and Random Forest

    Bagging和随机森林RF. 随机森林是最受欢迎和最强大的机器学习算法之一.它是一种称为Bootstrap Aggregation或bagging的集成机器学习算法. bootstrap是一种强大的 ...

  3. HTTP协议(待写)

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

  4. git的搭建和使用

    目录: 1.git与github介绍2.下载安装Git-20-64-bit.exe3.Git常用命令 git与github介绍 Git是什么 Git是一个开源的[分布式][版本控制系统],用于敏捷高效 ...

  5. Nodejs仿Apache的部分功能

    一.初步实现Apache的部分功能 //1.加载模块 var http=require('http'); var fs=require('fs'); //2.创建server var server=h ...

  6. Cogs 739. [网络流24题] 运输问题(费用流)

    [网络流24题] 运输问题 ★★ 输入文件:tran.in 输出文件:tran.out 简单对比 时间限制:1 s 内存限制:128 MB «问题描述: «编程任务: 对于给定的m 个仓库和n 个零售 ...

  7. 查全率(Recall),查准率(Precision),灵敏性(Sensitivity),特异性(Specificity),F1,PR曲线,ROC,AUC的应用场景

    之前介绍了这么多分类模型的性能评价指标(<分类模型的性能评价指标(Classification Model Performance Evaluation Metric)>),那么到底应该选 ...

  8. 洛谷P1043数字游戏

    题目 区间DP,将\(maxn[i][j][k]\)表示为i到j区间内分为k个区间所得到的最大值,\(minn\)表示最小值. 然后可以得到状态转移方程: \[maxn[i][j][k]= max(m ...

  9. 在Matlab中的tick可以调整方向

    需要将axis对话框的More property打开,修改TickDir,可从In改成Out.

  10. ASCII编码(以备不时之需)