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關鍵字 (我今天才知道)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章