單例模式——多線程——雙重鎖定

多線程的程序中,多個線程同時,注意是同時訪問Singleton類,調用GetInstance()方法,會有可能造成多個實例的。這個時候需要給進程加鎖來處理

Singleton類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton_Model
{
    class Singleton
    {
        //私有化一個自己引用
        private static Singleton instance;
        //程序運行時候創建一個靜態只讀的進程輔助對象
        private static readonly object syncRoot = new object();
        //私有化構造器 杜絕了外界利用New實例化本類的可能
        private Singleton()
        {

        }
        //此方法是獲得本類實例的唯一全局訪問點
        public static Singleton GetInstance()
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
            
            return instance;
        }
    }
}

缺點:每次調用Instance都需要Lock會很耗費性能

新的Singleton類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton_Model
{
    class Singleton
    {
        //私有化一個自己引用
        private static Singleton instance;
        //程序運行時候創建一個靜態只讀的進程輔助對象
        private static readonly object syncRoot = new object();
        //私有化構造器 杜絕了外界利用New實例化本類的可能
        private Singleton()
        {

        }
        //此方法是獲得本類實例的唯一全局訪問點
        public static Singleton GetInstance()
        {
            if (instance==null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            
            
            return instance;
        }
    }
}

這裏可以看到裏外兩層判斷!沒錯這裏沒有問題 !當Instance爲null並且同時有兩個線程調用GetInstance方法時,它們都將可以通過第一重的Instancenull的判斷,然後由於Lock機制,這兩個線程則只能有一個進入 另一個排隊等候。此時若是沒有第二重的Instancenull的判斷,則第一個線程創建了實例,而第二個線程還是可以繼續再創建新的實例,這樣就沒有達到單例的目的!!

靜態初始化:其實在實際的應用當中,C#與公共語言運行庫也提供了一種‘靜態初始化’的方法,這種方法不需要開發人員顯示的編寫線程安全的代碼,即可以解決多線程環境下它不是安全的問題!實現更簡單,但是談不上更好!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Singleton_Model
{
    //sealed阻止發生派生,而派生可能會增加實例。
    public sealed class Singleton1
    {
        private static readonly Singleton1 instance = new Singleton1();
        private Singleton1()
        {

        }
        public static Singleton1 GetInstance()
        {
            return instance;
        }
        /**
         * 
         * * 這樣的實現與前面的實例類似,也是解決了單例模式試圖解決的兩個基本問題,全局訪問和實例化控制,
         *  1 公共靜態屬性爲訪問實例提供了一個全局的訪問點。
         *  不同之處在於它依賴公共語言運行庫來初始化變量。由於構造方法是私有的,因此不能再類本身以外實例化
         *  Singleton1類。因此 變量引用的是可以在系統中存在的唯一的實例。不過要注意,instance變量標記爲readonly
         *  這意味着只能在靜態初始化期間或在類構造函數中分配【MSDN】。
         *  由於這種靜態初始化的方式是在自己被加載時就將自己實例化 所以形象的稱爲餓【漢式單例模式】
         *  原先的單例處理模式是要再第一次被引用時,纔會將自己實例化 所以被形象稱爲【懶漢單例模式】
         * 【漢式單例模式】 靜態初始化方式,它是類一加載就實例化的對象,所以要提前佔用系統資源
         * 【懶漢單例模式】 面臨着多線程訪問的安全問題 需要做雙重鎖定才能保證安全。
         * 所以要用哪一種 必須考慮實際的需求 C#【漢式單例模式】基本能滿足需求
         */
    }
}

這樣的實現與前面的實例類似,也是解決了單例模式試圖解決的兩個基本問題,全局訪問和實例化控制,
* 1 公共靜態屬性爲訪問實例提供了一個全局的訪問點。
* 不同之處在於它依賴公共語言運行庫來初始化變量。由於構造方法是私有的,因此不能再類本身以外實例化
* Singleton1類。因此 變量引用的是可以在系統中存在的唯一的實例。不過要注意,instance變量標記爲readonly
* 這意味着只能在靜態初始化期間或在類構造函數中分配【MSDN】。
* 由於這種靜態初始化的方式是在自己被加載時就將自己實例化 所以形象的稱爲餓【漢式單例模式】
* 原先的單例處理模式是要再第一次被引用時,纔會將自己實例化 所以被形象稱爲【懶漢單例模式】
* 【漢式單例模式】 靜態初始化方式,它是類一加載就實例化的對象,所以要提前佔用系統資源
* 【懶漢單例模式】 面臨着多線程訪問的安全問題 需要做雙重鎖定才能保證安全。
* 所以要用哪一種 必須考慮實際的需求 C#【漢式單例模式】基本能滿足需求

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