从单例模式看C#的volatile关键字

目录

  1. Singleton示例
  2. volatile解决问题1:CPU缓存
  3. volatile解决问题2:编译器优化(指令乱序)

一. 标准的单例模式示例
```csharp
public sealed class Singleton
{
// 静态实例
private static volatile Singleton instance = null;
// Lock对象,线程安全所用
private static object syncRoot = new Object();

private Singleton() { }

public static Singleton Instance
{
    get
    {
        if (instance == null)                            //一次比较
        {
            lock (syncRoot)
            {
                if (instance == null)                    //二次比较
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

}

```

注意静态示例自动前的修饰符: volatile,为什么必须指定volatile, 如果不使用该关键字,会有什么后果呢。

volatile【易变的】,查msdn:
volatile 关键字指示一个字段可以由多同时执行的线程修改。 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。

那在底层到底发生了什么,难道不使用volatile,指令执行时,字段的值还不是新的吗,这得从计算机架构及编译器优化两个方面说起。

二. CPU缓存问题

采用volatile关键字,每次读字段值时,都必须从内存中获取,也就是说,对该字段禁用CPU缓存。

由于现代的CPU都存在多个核心,每个核心有独立的内部缓存,而对象字段最初是保存在内存中的,执行指令前,会首先检查缓存中是否存在该字段,如果没有,从内存中读取数据到CPU缓存,进行运算, 如果缓存存在,则无需访问内存,直接使用缓存的数据。

针对单例模式示例,两个线程分别在两个CPU核心运行,两个核心同时运行到上面语句“第一次比较”,instance字段都保存在各个CPU的内部缓存中,通过lock关键字,两个线程会串行执行lock语句块。

比如核心A已经运行完成lock语句块,内存中的instance已经更新,此时核心B继续运行,由于核心B已经缓存了instance示例,在二次比较时,还是认为instance字段为空,这样就导致 new Singleton()执行了两次。

三. 编译器优化问题

采用volatile关键字, 可以避免指令重新排序(instruction reordering), 例如,考虑如下一个循环:
```csharp
while(true)
{
if(myField)
{
//do something
}
}

```

如果myField字段没有指定volatile, 在JIT编译时,出于性能优化考虑,编译器会以如下方式重新排序指令:
```csharp

if(myField)
{
while(true)
{
//do something
}
}

```

这种情况下,如果你在另一个线程中修改字段myFiled, 运行结果将完成不同。
通常情况下,推荐使用lock语句(Monitor.Enter /Monitor.Exit),但如果你在这个语句块内仅仅修改了一个字段,volatile将有更好的性能表现。

从单例模式看C#的volatile关键字

时间: 2024-11-01 05:42:28

从单例模式看C#的volatile关键字的相关文章

双重检查锁单例模式为什么要用volatile关键字?

前言 从Java内存模型出发,结合并发编程中的原子性.可见性.有序性三个角度分析volatile所起的作用,并从汇编角度大致说了volatile的原理,说明了该关键字的应用场景:在这补充一点,分析下volatile是怎么在单例模式中避免双检锁出现的问题的. 并发编程的3个条件 1.原子性:要实现原子性方式较多,可用synchronized.lock加锁,AtomicInteger等,但volatile关键字是无法保证原子性的:2.可见性:要实现可见性,也可用synchronized.lock,v

对Java单例模式 volatile关键字作用的理解

单例模式是程序设计中经常用到的,简单便捷的设计模式,也是很多程序猿对设计模式入门的第一节课.其中最经典的一种写法是: class Singleton { private volatile static Singleton instance;//volatile关键字防止指令重排 private Singleton(){} public static Singleton getInstance() { if ( instance == null ) { //一重判断 synchronized (S

从JAVA看C#中volatile和synchronized关键字的作用

最近一直在想C#中 volatile关键字到底是用来干什么的?查了很多.NET的文章都是说用volatile修饰的变量可以让多线程同时修改,这是什么鬼... 然后查到了下面这篇JAVA中关于volatile和synchronized关键字的概述,总算对volatile和synchronized关键字有了个大概的了解,而C#中应该类似,注意C#中没有synchronized关键字,但是有MethodImplAttribute 类 和 SynchronizationAttribute 类与JAVA中

单例模式中的volatile关键字

在之前学习了单例模式在多线程下的设计,疑惑为何要加volatile关键字.加与不加有什么区别呢?这里我们就来研究一下.单例模式的设计可以参考个人总结的这篇文章 ??背景:在早期的JVM中,synchronized存在巨大的性能开销.因此,有人想出了一个"聪明"的技巧:双重检查锁定(Double-Checked Locking).人们想通过双重检查锁定来降低同步的开销.下面是使用双重检查锁定来实现延迟初始化的示例代码. public class DoubleCheckedLocking

深入理解JVM读书笔记五: Java内存模型与Volatile关键字

12.2硬件的效率与一致性 由于计算机的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无须等待缓慢的内存读写了. 基于高速缓存的存储交互很好地理解了处理器与内存的速度矛盾,但是也为计算机系统带来了更高的复杂度,因为它引入了一个新的问题: 缓存一致性(Cache Coherenc

Java volatile关键字解惑

volatile特性 内存可见性:通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的. volatile的使用场景 通过关键字sychronize可以防止多个线程进入同一段代码,在某些特定场景中,volatile相当于一个轻量级的sychronize, 因为不会引起线程的上下文切换,但是使用volatile必须满足两个条件: 1.对变量的写操作不依赖当前值,如多线程下执行a++,是无法通过volatile保证结果准确性

java的单例模式,为什么需要volatile

目前看了java并发的书,记录一下.对于java的单例模式,正确的代码应该为: public class TestInstance { private volatile static TestInstance instance; public static TestInstance getInstance() { //1 if (instance == null) { //2 synchronized (TestInstance.class) {//3 if (instance == null)

java并发编程--深入理解volatile关键字

??volatile是一个在java并发编程中耳熟能详的关键字.即使从来没有使用过,你也偶尔会在技术书籍或博客中见到.对volatile关键字的解释常常被一笔带过:被修饰的变量具有可见性,但不能保证原子性.但是到底如何保证可见性,可见性是什么--诸如此类的问题在碰到这种凝练的解释时会产生一种知其然不知其所以然的困惑.那么我将尽力在这一篇文章中将volatile关键字的意义彻底击穿. 可见性 ??可见性,顾名思义,可见程度.假如你在被窝里打开了手电筒,只有你自己知道手电筒打开了,那么可见性就非常差

你真的了解volatile关键字吗?

volatile关键字经常在并发编程中使用,其特性是保证可见性以及有序性,但是关于volatile的使用仍然要小心,这需要明白volatile关键字的特性及实现的原理,这也是本篇文章的主要内容. 一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中. 每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来).线程对变量的所有操作(读取,赋值)