C#基础 关键字之volatile

volatile基础

msdn的解释:http://msdn.microsoft.com/zh-cn/library/x13ttww7.aspx

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

volatile 修饰符通常用于由多个线程访问但不使用 lock 语句对访问进行序列化的字段。有关在多线程方案中使用 volatile 的示例,请参见如何:创建和终止线程(C# 编程指南)

volatile 关键字可应用于以下类型的字段:

1.引用类型

2.指针类型(在不安全的上下文中)
请注意,虽然指针本身可以是可变的,但是它指向的对象不能是可变的。换句话说,您无法声明“指向可变对象的指针”。

3.整型
如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。

4.枚举类型
具有整数基类型的枚举类型

5.已知为引用类型的泛型类型参数。

6.IntPtr 和 UIntPtr。

PS:可变关键字仅可应用于类或结构字段。不能将局部变量声明为 volatile。

Terrylee的代码

using System;

namespace SigletonPattern.Sigleton
{
    /// <summary>
    /// 功?能?:?在?C#用?双?重?锁?定?实?现?单?件?模?式?
    /// 编?写?:?Terrylee
    /// 日?期?:?年?月?日?
    /// </summary>
    public class DoubLockSigleton
    {    

        private static volatile DoubLockSigleton instance;
        /// <summary>
        /// 辅?助?锁?对?象?,?本?身?没?有?意?义?
        /// </summary>
        private static object syncRoot = new Object();

        /// <summary>
        /// 构?造?方?法?改?为?Private
        /// </summary>
        private DoubLockSigleton()
        {
            
        }

        public static DoubLockSigleton Instance
        {
            get 
            {
                if (instance == null) 
                {
                    lock (syncRoot) 
                    {
                        if (instance == null) 
                            instance = new DoubLockSigleton();
                    }
                }

                return instance;
            }
        }

    }
}

volatile与synchronized

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}
geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改 变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程 可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值 是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
   而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经 volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量 存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
  既然volatile关键字已经实现了线程间数据同步,又要 synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监 视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“ 主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:

  1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
  2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
  3. 代码块被执行
  4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
  5. 线程释放监视this对象的对象锁
      因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。
      
    更通俗的解释:

Volatile 字面的意思时易变的,不稳定的。在C#中也差不多可以这样理解。

编译器在优化代码时,可能会把经常用到的代码存在Cache里面,然后下一次调用就直接读取Cache而不是内存,这样就大大提高了效率。但是问题也随之而来了。

在多线程程序中,如果把一个变量放入Cache后,又有其他线程改变了变量的值,那么本线程是无法知道这个变化的。它可能会直接读Cache里的数据。但是很不幸,Cache里的数据已经过期了,读出来的是不合时宜的脏数据。这时就会出现bug。

用Volatile声明变量可以解决这个问题。用Volatile声明的变量就相当于告诉编译器,我不要把这个变量写Cache,因为这个变量是可能发生改变的。

C#中volatile的用法
C# 中的volatile关键字 (我今天才知道)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值