《C#設計模式》【單例模式】

《C#設計模式》- 單例模式

深度解析六種單例模式實現方法,以及安全,性能分析

第一種:不是線程安全的

public  sealed  class  Singleton 
{ 
    private  static  Singleton instance = null ; 

    private  Singleton()
    { 
    } 

    public  static  Singleton Instance 
    { 
        get 
        { 
            if  (instance == null)
            { 
                instance =  new  Singleton(); 
            } 
            return  instance; 
        } 
    } 
}

上述內容不是線程安全的。兩個不同的線程都可以評估測試if (instance==null)並發現它爲true,然後兩個都創建實例,這違反了單例模式。請注意,實際上,在計算表達式之前可能已經創建了實例,但是內存模型不保證其他線程可以看到實例的新值,除非已經傳遞了合適的內存屏障(互斥鎖) 

 

第二種 :簡單線程安全

public sealed class Singleton
{
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}

此實現是線程安全的。線程取消對共享對象的鎖定,然後在創建實例之前檢查是否已創建實例。這會解決內存屏障問題(因爲鎖定確保在獲取鎖之後邏輯上發生所有讀取,並且解鎖確保在鎖定釋放之前邏輯上發生所有寫入)並確保只有一個線程將創建實例(僅限於一次只能有一個線程可以在代碼的那一部分中——當第二個線程進入它時,第一個線程將創建實例,因此表達式將計算爲false)。不幸的是,每次請求實例時都會獲得鎖定,因此性能會受到影響。

請注意,不是typeof(Singleton)像這個實現的某些版本那樣鎖定,而是鎖定一個私有的靜態變量的值。鎖定其他類可以訪問和鎖定的對象(例如類型)會導致性能問題甚至死鎖。這是我通常的風格偏好——只要有可能,只鎖定專門爲鎖定目的而創建的對象,或者爲了特定目的(例如,等待/觸發隊列)而鎖定的文檔。通常這些對象應該是它們所使用的類的私有對象。這有助於使編寫線程安全的應用程序變得更加容易。

第三種:雙重檢查鎖定嘗試線程安全

public sealed class Singleton
{
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

該實現嘗試是線程安全的,而不必每次都取出鎖。不幸的是,該模式有四個缺點:

  • 它在Java中不起作用。這似乎是一個奇怪的事情,但是如果您需要Java中的單例模式,這是值得知道的,C#程序員也可能是Java程序員。Java內存模型無法確保構造函數在將新對象的引用分配給Instance之前完成。Java內存模型經歷了1.5版本的重新改進,但是在沒有volatile變量(如在C#中)的情況下,雙重檢查鎖定仍然會被破壞。
  • 在沒有任何內存障礙的情況下,ECMA CLI規範也打破了這一限制。有可能在.NET 2.0內存模型(比ECMA規範更強)下它是安全的,但我寧願不依賴那些更強大的語義,特別是如果對安全性有任何疑問的話。使instance變量volatile變得有效,就像明確的內存屏障調用一樣,儘管在後一種情況下,甚至專家也無法準確地就需要哪些屏障達成一致。我儘量避免專家對對錯意見也不一致的情況!
  • 這很容易出錯。該模式需要完全如上所述——任何重大變化都可能影響性能或正確性。
  • 它的性能仍然不如後續的實現。

第四種:飽漢式不使用鎖且線程安全

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // 顯式靜態構造函數告訴C#編譯器
    static Singleton()
    {
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

C#中的靜態構造函數僅在創建類的實例或引用靜態成員時執行,並且每個AppDomain只執行一次。考慮到無論發生什麼情況,都需要執行對新構造的類型的檢查,這比在前面的示例中添加額外檢查要快。然而,還有一些小缺陷:

  • 它並不像其他實現那樣懶惰。特別是,如果您有Instance之外的靜態成員,那麼對這些成員的第一次引用將涉及到創建實例。這將在下一個實現中得到糾正。
  • 如果一個靜態構造函數調用另一個靜態構造函數,而另一個靜態構造函數再次調用第一個構造函數,則會出現複雜情況。
  • 類型初始化器的懶惰性只有在.NET沒有使用名爲BeforeFieldInit的特殊標誌標記類型時才能得到保證。不幸的是,C#編譯器,將所有沒有靜態構造函數的類型。

 

 

第五種:餓漢式,鎖且線程安全

public sealed class Singleton
{
    private Singleton()
    {
    }

    public static Singleton Instance { get { return Nested.instance; } }
        
    private class Nested
    {
        // 顯式靜態構造告訴C#編譯器
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

在這裏,實例化是由對嵌套類的靜態成員的第一次引用觸發的,該引用只發生在Instance中。這意味着實現是完全懶惰的,但是具有前面實現的所有性能優勢。請注意,儘管嵌套類可以訪問封閉類的私有成員,但反之則不然,因此需要instance在此處爲內部成員。不過,這不會引起任何其他問題,因爲類本身是私有的。但是,爲了使實例化變得懶惰,代碼要稍微複雜一些。

 

第六種:使用.NET 4的 Lazy 類型【延遲加載對象】

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());
    
    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

它很簡單,而且性能很好。它還允許您檢查是否已使用IsValueCreated 屬性創建實例(如果需要的話)。

上面的代碼隱式地將LazyThreadSafetyMode.ExecutionAndPublication用作Lazy<Singleton>的線程安全模式。根據您的要求,您可能希望嘗試其他模式。

本文案例分析來源

 

 

 

 

 

 

發佈了24 篇原創文章 · 獲贊 21 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章