内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术_chuhan0449的博客-CSDN博客

我们经常都听到并发编程,但很多人都被其高大上的感觉迷惑而停留在知道听说这一层面,下面我们就来讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术。

CPU使用了很多优化技术来实现一个目标:CPU执行单元的速度要远超主存访问速度。CPU避免内存访问延迟最常见的技术是将指令管道化,然后尽量重排这些管道的执行以最大化利用缓存,从而把因为缓存未命中引起的延迟降到最小。

当一个程序执行时,只要最终的结果是一样的,指令是否被重排并不重要。例如,在一个循环里,如果循环体内没用到这个计数器,循环的计数器什么时候更新(在循环开始,中间还是最后)并不重要。编译器和CPU可以自由的重排指令以最佳的利用CPU,只要下一次循环前更新该计数器即可。并且在循环执行中,这个变量可能一直存在寄存器上,并没有被推到缓存或主存,这样这个变量对其他CPU来说一直都是不可见的。

CPU核内部包含了多个执行单元。例如,INTEL CPU包含了6个执行单元,可以组合进行算术运算,逻辑条件判断及内存操作。每个执行单元可以执行上述任务的某种组合。这些执行单元是并行执行的,这样指令也就是在并行执行。但如果站在另一个CPU角度看,这也就产生了程序顺序的另一种不确定性。

最后,当一个缓存失效发生时,CPU可以先假设一个内存载入的值并根据这个假设值继续执行,直到内存载入返回确切的值。

代码顺序并不是真正的执行顺序,只要有空间提高性能,CPU和编译器可以进行各种优化。缓存和主存的读取会利用load, store和write-combining缓冲区来缓冲和重排。这些缓冲区是查找速度很快的关联队列,当一个后来发生的load需要读取上一个 store的值,而该值还没有到达缓存,查找是必需的,上图描绘的是一个简化的现代多核CPU,从上图可以看出执行单元可以利用本地寄存器和缓冲区来管理 和缓存子系统的交互。

在多线程环境里需要使用某种技术来使程序结果尽快可见。这篇文章里我不会涉及到 Cache Conherence 的概念。请先假定一个事实:一旦内存数据被推送到缓存,就会有消息协议来确保所有的缓存会对所有的共享数据同步并保持一致。这个使内存数据对CPU核可见 的技术被称为内存屏障或内存栅栏

内存屏障提供了两个功能。首先,它们通过确保从另一个CPU来看屏障的两边的所有指令都是正确的程序顺序,而保持程序顺序的外部可见性;其次它们可以实现内存数据可见性,确保内存数据会同步到CPU缓存子系统。

大多数的内存屏障都是复杂的话题。在不同的CPU架构上内存屏障的实现非常不一样。相对来说Intel CPU的强内存模型比DEC Alpha的弱复杂内存模型(缓存不仅分层了,还分区了)更简单。因为x86处理器是在多线程编程中最常见的,下面我尽量用x86的架构来阐述。

Store Barrier

Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存。这会使得程序状态对其它CPU可见,这样其它CPU可以根据需要介入。一个实际的好例子是Disruptor中的BatchEventProcessor。当序列Sequence被一个消费者更新时,其它消费者(Consumers)和生产者(Producers)知道该消费者的进度,因此可以采取合适的动作。所以屏障之前发生的内存更新都可见了。

private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE;

// from inside the run() method

T event = null;

long nextSequence = sequence.get() + 1L;

while (running)

{

    try

    {

        final long availableSequence = barrier.waitFor(nextSequence);

        while (nextSequence <= availableSequence)

        {

            event = ringBuffer.get(nextSequence);

            boolean endOfBatch = nextSequence == availableSequence;

            eventHandler.onEvent(event, nextSequence, endOfBatch);

            nextSequence++;

        }

        sequence.set(nextSequence - 1L);

        // store barrier inserted here !!!

    }

    catch (final Exception ex)

    {

        exceptionHandler.handle(ex, nextSequence, event);

        sequence.set(nextSequence);

        // store barrier inserted here !!!

        nextSequence++;

    }

}

Load Barrier

Load屏障,是x86上的”ifence“指令,强制所有在load屏障指令之后的load指令,都在该 load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令。这使得从其它CPU暴露出来的程序状态对该 CPU可见,这之后CPU可以进行后续处理。一个好例子是上面的BatchEventProcessor的sequence对象是放在屏障后被生产者或消 费者使用。

Full Barrier

Full屏障,是x86上的”mfence“指令,复合了load和save屏障的功能。

Java内存模型

Java内存模型中volatile变量在写操作之后会插入一个store屏障,在读操作之前会插入一个load屏障。一个类的final字段会在初始化后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见。

原子指令和Software Locks

原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。

内存屏障的性能影响

内存屏障阻碍了CPU采用优化技术来降低内存操作延迟,必须考虑因此带来的性能损失。为了达到最佳性能,最好是把要解决的问题模块化,这样处理器可 以按单元执行任务,然后在任务单元的边界放上所有需要的内存屏障。采用这个方法可以让处理器不受限的执行一个任务单元。合理的内存屏障组合还有一个好处 是:缓冲区在第一次被刷后开销会减少,因为再填充改缓冲区不需要额外工作了。

转载于:https://my.oschina.net/quicker/blog/411997

内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术相关推荐

  1. java内存栅栏_内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术

    我们经常都听到并发编程,但很多人都被其高大上的感觉迷惑而停留在知道听说这一层面,下面我们就来讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见 ...

  2. 内存屏障 Memory Barriers

    内存屏障 Memory Barriers 在上一篇文章中我们提到了编译时的内存序重排导致的问题以及解决方法,即添加编译器屏障或处理器屏障指令.这篇文章将探讨内存屏障的语义. 内存屏障的类型 Types ...

  3. 多核心CPU并行编程中为什么要使用内存屏障 memory barriers / 内存栅栏 memory fence

    文章目录 前言 现代Intel® CPU架构 指令集 CISC, RICS ... Intel各个时期的CPU微架构(microarchitecture)特点 P6 Family Microarchi ...

  4. volatile关键字——保证并发编程中的可见性、有序性

    文章目录 一.缓存一致性问题 二.并发编程中的三个概念 三.Java线程内存模型 1.原子性 2.可见性 3.有序性 四.深入剖析volatile关键字 1.volatile关键字的两层语义 2.vo ...

  5. Java并发编程中的若干核心技术,向高手进阶

    来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...

  6. Go并发编程中的那些事[译]

    原文地址:Concurrent programming 原文作者:StefanNilsson 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:kobehah ...

  7. Java的并发编程中的多线程问题到底是怎么回事儿?

    转载自   Java的并发编程中的多线程问题到底是怎么回事儿? 在我之前的一篇<再有人问你Java内存模型是什么,就把这篇文章发给他.>文章中,介绍了Java内存模型,通过这篇文章,大家应 ...

  8. synchronized 异常_由浅入深,Java 并发编程中的 Synchronized

    synchronized 作用 synchronized 关键字是 Java 并发编程中线程同步的常用手段之一. 1.1 作用: 确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须 ...

  9. java 线程由浅入深_由浅入深,Java 并发编程中的 Synchronized(一)

    synchronized 作用 synchronized 关键字是 Java 并发编程中线程同步的常用手段之一. 1.1 作用: 确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须 ...

最新文章

  1. TC260-001《汽车采集数据处理安全指南》
  2. 2017年10月08日普及组 蜡烛
  3. 牛客 - 合约数(树上启发式合并)
  4. 自适应小波阈值去噪python_基于python的小波阈值去噪算法
  5. c 子类对象 访问父类对象受保护成员_C++日志(三十四)子类同名成员与作用域分辨符...
  6. CSS2-3常见的demo列子总结
  7. Windows Phone 使用 HttpWebRequest 对象,通过POST 方式上传图片
  8. H3C 路由过滤与路由引入
  9. python中oserror winerror,在python中将WindowsError转换为OSError
  10. 【直线检测】基于LSD实现直线检测含Matlab源码
  11. Java工程师是做什么的?学习java能干什么?
  12. 怎么学好Web前端开发 有哪些相关书籍推荐
  13. 吕蒙正千年奇文《寒窑赋》鉴赏
  14. 如何高效进行出货复核作业,提升出货准确率?
  15. Unity特效基础:简易爆炸效果
  16. 格里高历日历判断闰年
  17. Directory Opus Pro v12.29.8272 文件管理资源管理器工具专业版
  18. MFC 的 Picture Control 加载 BMP/PNG 图片的方法
  19. excel数字后边添加单位
  20. 基于vue2全家桶开发的匿名朋友圈及聊天应用

热门文章

  1. Linux中在zsh下如何安装autojump
  2. 内网渗透--基于密码的破解提权
  3. HDFS系统中Browse Directory目录显示WebHDFS已禁用
  4. golang 管道_必须具有用于golang构建管道的工具
  5. 找不到引用microsoft.office.core解决办法
  6. springboot达成tar包
  7. 数据结构3:时间复杂度计算举例
  8. C# winform点击某个窗口报错--调用目标发生异常
  9. 读书计划-2008年12月
  10. SpringCloud: 路由网关(zuul)