Disruptor-NET和内存栅栏
Disruptor-NET算法(是一种无锁算法)需要我们自己实现某一种特定的内存操作的语义以保证算法的正确性。这时我们就需要显式的使用一些指令来控制内存操作指令的顺序以及其可见性定义。这种指令称为内存栅栏。
内存一致性模型需要在各种的程序与系统的各个层次上定义内存访问的行为。在机器码与的层次上,其定义将影响硬件的设计者以及机器码开发人员;而在高级语言层次上,其定义将影响高级语言开发人员以及编译器开发人员和硬件设计人员。即,内存操作的乱序在各个层次都是存在的。这里,所谓的程序的执行顺序有三种:
(1)程序顺序:指在特定CPU上运行的,执行内存操作的代码的顺序。这指的是编译好的程序二进制镜像中的指令的顺序。编译器并不一定严格按照程序的顺序进行二进制代码的编排。编译器可以按照既定的规则,在执行代码优化的时候打乱指令的执行顺序,也就是上面说的程序顺序。并且,编译器可以根据程序的特定行为进行性能优化,这种优化可能改变算法的形式与算法的执行复杂度。(例如将switch转化为表驱动序列)
(2)执行顺序:指在CPU上执行的独立的内存相关的代码执行的顺序。执行顺序和程序顺序可能不同,这种不同是编译器和CPU优化造成的结果。CPU在执行期(Runtime)根据自己的内存模型(跟编译器无关)打乱已经编译好了的指令的顺序,以达到程序的优化和最大限度的资源利用。
(3)感知顺序:指特定的CPU感知到他自身的或者其他CPU对内存进行操作的顺序。感知顺序和执行顺序可能还不一样。这是由于缓存优化或者内存优化系统造成的。
而最终的共享内存模型的表现形式是由这三种“顺序”共同决定的。即从源代码到最终执行进行了至少三个层次上的代码顺序调整,分别是由编译器和CPU完成的。我们上面提到,这种代码执行顺序的改变虽然在单线程程序中不会引发副作用,但是在多线程程序中,这种作用是不能够被忽略的,甚至可能造成完全错误的结果。因此,在多线程程序中,我们有时需要人为的限制内存执行的顺序。而这种限制是通过不同层次的内存栅栏完成的。
Thread.MemoryBarrier就是采用了CPU提供的某些特定的指令的内存栅栏,下面是msdn的解释【http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.thread.memorybarrier(v=vs.100).aspx】:
Thread.MemoryBarrier: 按如下方式同步内存访问:执行当前线程的处理器在对指令重新排序时,不能采用先执行 MemoryBarrier 调用之后的内存访问,再执行 MemoryBarrier 调用之前的内存访问的方式。
按照我个人的理解:就是写完数据之后,调用MemoryBarrier,数据就会立即刷新,另外在读取数据之前调用MemoryBarrier可以确保读取的数据是最新的,并且处理器对MemoryBarrier的优化小心处理。
int _answer;
bool _complete; void A()
{
_answer = 123;
Thread.MemoryBarrier(); //在写完之后,创建内存栅栏
_complete = true;
Thread.MemoryBarrier();//在写完之后,创建内存栅栏
} void B()
{
Thread.MemoryBarrier();//在读取之前,创建内存栅栏
if (_complete)
{
Thread.MemoryBarrier();//在读取之前,创建内存栅栏
Console.WriteLine(_answer);
}
}
Disruptor-NET正是通过Thread.MemoryBarrier 实现无锁和线程安全的内存操作,看下面是他的Atomic的Volatile类对常用数据类型的封装,volatile可以阻止代码重排序,并且值被更新的时候,会导致缓存失效,强制回写到主存中。

/// <summary>
/// An integer value that may be updated atomically
/// </summary>
public struct Integer
{
private int _value; /// <summary>
/// Create a new <see cref="Integer"/> with the given initial value.
/// </summary>
/// <param name="value">Initial value</param>
public Integer(int value)
{
_value = value;
} /// <summary>
/// Read the value without applying any fence
/// </summary>
/// <returns>The current value</returns>
public int ReadUnfenced()
{
return _value;
} /// <summary>
/// Read the value applying acquire fence semantic
/// </summary>
/// <returns>The current value</returns>
public int ReadAcquireFence()
{
var value = _value;
Thread.MemoryBarrier();
return value;
} /// <summary>
/// Read the value applying full fence semantic
/// </summary>
/// <returns>The current value</returns>
public int ReadFullFence()
{
var value = _value;
Thread.MemoryBarrier();
return value;
} /// <summary>
/// Read the value applying a compiler only fence, no CPU fence is applied
/// </summary>
/// <returns>The current value</returns>
[MethodImpl(MethodImplOptions.NoOptimization)]
public int ReadCompilerOnlyFence()
{
return _value;
} /// <summary>
/// Write the value applying release fence semantic
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteReleaseFence(int newValue)
{
_value = newValue;
Thread.MemoryBarrier();
} /// <summary>
/// Write the value applying full fence semantic
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteFullFence(int newValue)
{
_value = newValue;
Thread.MemoryBarrier();
} /// <summary>
/// Write the value applying a compiler fence only, no CPU fence is applied
/// </summary>
/// <param name="newValue">The new value</param>
[MethodImpl(MethodImplOptions.NoOptimization)]
public void WriteCompilerOnlyFence(int newValue)
{
_value = newValue;
} /// <summary>
/// Write without applying any fence
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteUnfenced(int newValue)
{
_value = newValue;
} /// <summary>
/// Atomically set the value to the given updated value if the current value equals the comparand
/// </summary>
/// <param name="newValue">The new value</param>
/// <param name="comparand">The comparand (expected value)</param>
/// <returns></returns>
public bool AtomicCompareExchange(int newValue, int comparand)
{
return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
} /// <summary>
/// Atomically set the value to the given updated value
/// </summary>
/// <param name="newValue">The new value</param>
/// <returns>The original value</returns>
public int AtomicExchange(int newValue)
{
return Interlocked.Exchange(ref _value, newValue);
} /// <summary>
/// Atomically add the given value to the current value and return the sum
/// </summary>
/// <param name="delta">The value to be added</param>
/// <returns>The sum of the current value and the given value</returns>
public int AtomicAddAndGet(int delta)
{
return Interlocked.Add(ref _value, delta);
} /// <summary>
/// Atomically increment the current value and return the new value
/// </summary>
/// <returns>The incremented value.</returns>
public int AtomicIncrementAndGet()
{
return Interlocked.Increment(ref _value);
} /// <summary>
/// Atomically increment the current value and return the new value
/// </summary>
/// <returns>The decremented value.</returns>
public int AtomicDecrementAndGet()
{
return Interlocked.Decrement(ref _value);
} /// <summary>
/// Returns the string representation of the current value.
/// </summary>
/// <returns>the string representation of the current value.</returns>
public override string ToString()
{
var value = ReadFullFence();
return value.ToString();
}
}
/// <summary>
/// An integer value that may be updated atomically and is guaranteed to live on its own cache line (to prevent false sharing)
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 2)]
public struct PaddedInteger
{
[FieldOffset(CacheLineSize)]
private int _value; /// <summary>
/// Create a new <see cref="PaddedInteger"/> with the given initial value.
/// </summary>
/// <param name="value">Initial value</param>
public PaddedInteger(int value)
{
_value = value;
} /// <summary>
/// Read the value without applying any fence
/// </summary>
/// <returns>The current value</returns>
public int ReadUnfenced()
{
return _value;
} /// <summary>
/// Read the value applying acquire fence semantic
/// </summary>
/// <returns>The current value</returns>
public int ReadAcquireFence()
{
var value = _value;
Thread.MemoryBarrier();
return value;
} /// <summary>
/// Read the value applying full fence semantic
/// </summary>
/// <returns>The current value</returns>
public int ReadFullFence()
{
var value = _value;
Thread.MemoryBarrier();
return value;
} /// <summary>
/// Read the value applying a compiler only fence, no CPU fence is applied
/// </summary>
/// <returns>The current value</returns>
[MethodImpl(MethodImplOptions.NoOptimization)]
public int ReadCompilerOnlyFence()
{
return _value;
} /// <summary>
/// Write the value applying release fence semantic
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteReleaseFence(int newValue)
{
_value = newValue;
Thread.MemoryBarrier();
} /// <summary>
/// Write the value applying full fence semantic
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteFullFence(int newValue)
{
_value = newValue;
Thread.MemoryBarrier();
} /// <summary>
/// Write the value applying a compiler fence only, no CPU fence is applied
/// </summary>
/// <param name="newValue">The new value</param>
[MethodImpl(MethodImplOptions.NoOptimization)]
public void WriteCompilerOnlyFence(int newValue)
{
_value = newValue;
} /// <summary>
/// Write without applying any fence
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteUnfenced(int newValue)
{
_value = newValue;
} /// <summary>
/// Atomically set the value to the given updated value if the current value equals the comparand
/// </summary>
/// <param name="newValue">The new value</param>
/// <param name="comparand">The comparand (expected value)</param>
/// <returns></returns>
public bool AtomicCompareExchange(int newValue, int comparand)
{
return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
} /// <summary>
/// Atomically set the value to the given updated value
/// </summary>
/// <param name="newValue">The new value</param>
/// <returns>The original value</returns>
public int AtomicExchange(int newValue)
{
return Interlocked.Exchange(ref _value, newValue);
} /// <summary>
/// Atomically add the given value to the current value and return the sum
/// </summary>
/// <param name="delta">The value to be added</param>
/// <returns>The sum of the current value and the given value</returns>
public int AtomicAddAndGet(int delta)
{
return Interlocked.Add(ref _value, delta);
} /// <summary>
/// Atomically increment the current value and return the new value
/// </summary>
/// <returns>The incremented value.</returns>
public int AtomicIncrementAndGet()
{
return Interlocked.Increment(ref _value);
} /// <summary>
/// Atomically increment the current value and return the new value
/// </summary>
/// <returns>The decremented value.</returns>
public int AtomicDecrementAndGet()
{
return Interlocked.Decrement(ref _value);
} /// <summary>
/// Returns the string representation of the current value.
/// </summary>
/// <returns>the string representation of the current value.</returns>
public override string ToString()
{
var value = ReadFullFence();
return value.ToString();
}
}
PaddedInteger类,它使用了7个Integer,加上一个对象头,刚好64个字节。
剖析Disruptor:为什么会这么快?(二)神奇的缓存行填充
Disruptor-NET和内存栅栏的更多相关文章
- 第二节:深入剖析Thread的五大方法、数据槽、内存栅栏。
一. Thread及其五大方法 Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task(后面章节介绍),但从多线程完整性的角度上来 ...
- 内存栅栏(memory barrier):解救peterson算法的应用陷阱
最近一个项目中用到了peterson算法来做临界区的保护,简简单单的十几行代码,就能实现两个线程对临界区的无锁访问,确实很精炼.但是在这不是来分析peterson算法的,在实际应用中发现peterso ...
- memory barrier 内存栅栏 并发编程
并发编程 memory barrier (内存栅栏) CPU级 1.CPU中有多条流水线,执行代码时,会并行进行执行代码,所以CPU需要把程序指令 分配给每个流水线去分别执行,这个就是乱序执行: 2. ...
- 强如 Disruptor 也发生内存溢出?
前言 OutOfMemoryError 问题相信很多朋友都遇到过,相对于常见的业务异常(数组越界.空指针等)来说这类问题是很难定位和解决的. 本文以最近碰到的一次线上内存溢出的定位.解决问题的方式展开 ...
- Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案
本文翻译自LMAX关于Disruptor的论文,同时加上一些自己的理解和标注.Disruptor是一个高效的线程间交换数据的基础组件,它使用栅栏(barrier)+序号(Sequencing)机制协调 ...
- 从零开始实现lmax-Disruptor队列(六)Disruptor 解决伪共享、消费者优雅停止实现原理解析
MyDisruptor V6版本介绍 在v5版本的MyDisruptor实现DSL风格的API后.按照计划,v6版本的MyDisruptor作为最后一个版本,需要对MyDisruptor进行最终的一些 ...
- JVM内存模型、指令重排、内存屏障概念解析
在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...
- 内存管理_深入剖析volatile关键字
四.深入剖析volatile关键字 在前面讲述了很多东西,其实都是为讲述volatile关键字作铺垫,那么接下来我们就进入主题. 1.volatile关键字的两层语义 一旦一个共享变量(类的成员变量. ...
- Java内存访问重排序笔记
>>关于重排序 重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段. 重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境. > ...
随机推荐
- 从匿名方法到 Lambda 表达式的推演过程
Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数. 以上是msdn官网对Lambda 表达式 ...
- Tensorflow 处理libsvm格式数据生成TFRecord (parse libsvm data to TFRecord)
#写libsvm格式 数据 write libsvm #!/usr/bin/env python #coding=gbk # ================================= ...
- Android Studio中Button等控件的Text中字符串默认大写的解决方法
初学Android的时候,在Android Studio中xml里面添加一个Button.EditText等控件后,它的Text总是会显示大写,即使你输入的字符串是小写也不行,控制字符串大小写的属性是 ...
- 【Java EE 学习 71 上】【数据采集系统第三天】【增加页面】【增加问题】【编辑页面,编辑问题】
增加页面和编辑页面.增加问题和编辑问题的页面使用的都是相同的页面,最后调用的方法是saveOrUpdate方法,所以只说一个就可以了. 一.增加页面 比较简单,略.流程如下: 单击“增加页”超链接-& ...
- Chrome Extension 检查视图(无效)处理方法
最近闲来无事,简单看了下Chrome扩展的开发,并且开发一个小小的翻译插件(TranslateBao)作为练手,开发细节不详述了,如果有新学习chrome extension开发的新人,可以参考源码, ...
- POJ 3009 Curling 2.0【带回溯DFS】
POJ 3009 题意: 给出一个w*h的地图,其中0代表空地,1代表障碍物,2代表起点,3代表终点,每次行动可以走多个方格,每次只能向附近一格不是障碍物的方向行动,直到碰到障碍物才停下来,此时障碍物 ...
- 【积累篇:他山之石,把玉攻】Mime 类型列表
按照内容类型排列的 Mime 类型列表 类型/子类型 扩展名 application/envoy evy application/fractals fif application/futurespla ...
- 关于P,V操作理解的突破,关于并发设计与并行
今天又找了一篇博客研究P,V操作.. 发现..它有一个变量没有声明.. 我就换了篇博客..http://c.biancheng.net/cpp/html/2600.html 然后就看懂了.. 关键突破 ...
- 【Gson】互相转化
Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库.可以将一个 JSON 字符串转成一个 Java 对象,或者反过来. 对象转为字符串 Strin ...
- 解决ScrollView 嵌套 GridView 单行显示问题
简单重写GridView package com.hh.beauter.my_ui; import android.content.Context; import android.util.Attri ...