博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Disruptor-NET和内存栅栏
阅读量:6413 次
发布时间:2019-06-23

本文共 6436 字,大约阅读时间需要 21 分钟。

算法(是一种无锁算法)需要我们自己实现某一种特定的内存操作的语义以保证算法的正确性。这时我们就需要显式的使用一些指令来控制内存操作指令的顺序以及其可见性定义。这种指令称为内存栅栏。

内存一致性模型需要在各种的程序与系统的各个层次上定义内存访问的行为。在机器码与的层次上,其定义将影响硬件的设计者以及机器码开发人员;而在高级语言层次上,其定义将影响高级语言开发人员以及编译器开发人员和硬件设计人员。即,内存操作的乱序在各个层次都是存在的。这里,所谓的程序的执行顺序有三种:

(1)程序顺序:指在特定CPU上运行的,执行内存操作的代码的顺序。这指的是编译好的程序二进制镜像中的指令的顺序。编译器并不一定严格按照程序的顺序进行二进制代码的编排。编译器可以按照既定的规则,在执行代码优化的时候打乱指令的执行顺序,也就是上面说的程序顺序。并且,编译器可以根据程序的特定行为进行性能优化,这种优化可能改变算法的形式与算法的执行复杂度。(例如将switch转化为表驱动序列) 

(2)执行顺序:指在CPU上执行的独立的内存相关的代码执行的顺序。执行顺序和程序顺序可能不同,这种不同是编译器和CPU优化造成的结果。CPU在执行期(Runtime)根据自己的内存模型(跟编译器无关)打乱已经编译好了的指令的顺序,以达到程序的优化和最大限度的资源利用。

(3)感知顺序:指特定的CPU感知到他自身的或者其他CPU对内存进行操作的顺序。感知顺序和执行顺序可能还不一样。这是由于缓存优化或者内存优化系统造成的。

而最终的共享内存模型的表现形式是由这三种“顺序”共同决定的。即从源代码到最终执行进行了至少三个层次上的代码顺序调整,分别是由编译器和CPU完成的。我们上面提到,这种代码执行顺序的改变虽然在单线程程序中不会引发副作用,但是在多线程程序中,这种作用是不能够被忽略的,甚至可能造成完全错误的结果。因此,在多线程程序中,我们有时需要人为的限制内存执行的顺序。而这种限制是通过不同层次的内存栅栏完成的。

Thread.MemoryBarrier就是采用了CPU提供的某些特定的指令的内存栅栏,下面是msdn的解释【】:

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);            }        }

正是通过Thread.MemoryBarrier 实现无锁和线程安全的内存操作,看下面是他的Atomic的Volatile类对常用数据类型的封装

///         /// An integer value that may be updated atomically        ///         public struct Integer        {            private int _value;            ///             /// Create a new 
with the given initial value. ///
/// Initial value public Integer(int value) { _value = value; } /// /// Read the value without applying any fence /// ///
The current value
public int ReadUnfenced() { return _value; } /// /// Read the value applying acquire fence semantic /// ///
The current value
public int ReadAcquireFence() { var value = _value; Thread.MemoryBarrier(); return value; } /// /// Read the value applying full fence semantic /// ///
The current value
public int ReadFullFence() { var value = _value; Thread.MemoryBarrier(); return value; } /// /// Read the value applying a compiler only fence, no CPU fence is applied /// ///
The current value
[MethodImpl(MethodImplOptions.NoOptimization)] public int ReadCompilerOnlyFence() { return _value; } /// /// Write the value applying release fence semantic /// /// The new value public void WriteReleaseFence(int newValue) { _value = newValue; Thread.MemoryBarrier(); } /// /// Write the value applying full fence semantic /// /// The new value public void WriteFullFence(int newValue) { _value = newValue; Thread.MemoryBarrier(); } /// /// Write the value applying a compiler fence only, no CPU fence is applied /// /// The new value [MethodImpl(MethodImplOptions.NoOptimization)] public void WriteCompilerOnlyFence(int newValue) { _value = newValue; } /// /// Write without applying any fence /// /// The new value public void WriteUnfenced(int newValue) { _value = newValue; } /// /// Atomically set the value to the given updated value if the current value equals the comparand /// /// The new value /// The comparand (expected value) ///
public bool AtomicCompareExchange(int newValue, int comparand) { return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand; } /// /// Atomically set the value to the given updated value /// /// The new value ///
The original value
public int AtomicExchange(int newValue) { return Interlocked.Exchange(ref _value, newValue); } /// /// Atomically add the given value to the current value and return the sum /// /// The value to be added ///
The sum of the current value and the given value
public int AtomicAddAndGet(int delta) { return Interlocked.Add(ref _value, delta); } /// /// Atomically increment the current value and return the new value /// ///
The incremented value.
public int AtomicIncrementAndGet() { return Interlocked.Increment(ref _value); } /// /// Atomically increment the current value and return the new value /// ///
The decremented value.
public int AtomicDecrementAndGet() { return Interlocked.Decrement(ref _value); } /// /// Returns the string representation of the current value. /// ///
the string representation of the current value.
public override string ToString() { var value = ReadFullFence(); return value.ToString(); } }

 

本文转自 张善友 51CTO博客,原文链接:http://blog.51cto.com/shanyou/1346882,如需转载请自行联系原作者
你可能感兴趣的文章
js使用正则表达式判断手机和固话格式
查看>>
计算机是怎么存储数字的
查看>>
mysql简单的单表查询详解
查看>>
mysql性能优化之Tuning-primer.sh的使用
查看>>
Unix的发展史
查看>>
CentOS改变docker默认存储池大小
查看>>
Docker存储驱动devicemapper介绍和配置
查看>>
win2008作为个人电脑用需要优化的部分
查看>>
vi教程
查看>>
yum 本地源配置问题
查看>>
从Vue.js窥探前端行业
查看>>
Linux chown改变文件所属关系命令
查看>>
android开发——获取手机SD卡的容量
查看>>
django ajax提交 Forbidden CSRF token missing
查看>>
maven常见异常
查看>>
shell基础一
查看>>
windows下查看端口占用情况
查看>>
轻松玩转window7之五:管理共享
查看>>
邮件服务器搭建,可连接客户端
查看>>
大数据时代的遨游
查看>>