設計模式 - 18)單例模式

class STWindow
{
    static Window win;
    private STWindow() { }
        
    public static Window GetInstance()
    {
        if (win == null || !win.IsVisible)
        {
            win = new Window();
            win.Height = 200;
            win.Width = 200;
        }
                    
        return win;
    }        
}

class Singleton
{
    static Singleton _singleton;
    private Singleton() { }

    public static Singleton GetInstance()
    {
        if (_singleton == null)
        {
            _singleton = new Singleton();
        }
        return _singleton;
    }
}

1. 多線程同時建立實例問題

多個線程同時訪問 Singleton 的 GetInstance() 方法,可能會形成建立多個實例;
好比 A 先進入後在準備實例化 Singleton 的過程當中,B 也進入了,這時 _singleton 仍是爲 null,而後 B 也去實例化 Singleton 了。多線程

class Singleton
{
    static Singleton _singleton;
    private Singleton() { }

    public static Singleton GetInstance()
    {
        if (_singleton == null)
        {
            Console.WriteLine("實例化一個 Singleton");
            _singleton = new Singleton();
        }
        Console.WriteLine("返回一個 Singleton");
        return _singleton;
    }
}

System.Threading.Thread a = new System.Threading.Thread(() =>
{
    Singleton.GetInstance();
});

System.Threading.Thread b = new System.Threading.Thread(() =>
{
    Singleton.GetInstance();
});

System.Threading.Thread c = new System.Threading.Thread(() =>
{
    Singleton.GetInstance();
    });

a.Start();
b.Start();
c.Start();
實例化一個 Singleton
實例化一個 Singleton
返回一個 Singleton
返回一個 Singleton
返回一個 Singleton

咱們經過加個鎖來解決這個問題。性能

public static readonly object syncRoot = new object();

public static Singleton GetInstance()
{
    lock (syncRoot)
    {
        if (_singleton == null)
        {
            Console.WriteLine("實例化一個 Singleton");
            _singleton = new Singleton();
        }
    }
            
    Console.WriteLine("返回一個 Singleton");
    return _singleton;
}
實例化一個 Singleton
返回一個 Singleton
返回一個 Singleton
線程 0x1a08 已退出,返回值爲 0 (0x0)。
返回一個 Singleton
線程 0x45b4 已退出,返回值爲 0 (0x0)。
線程 0x5a04 已退出,返回值爲 0 (0x0)。

2. 每次實例都要鎖的性能問題

使用雙重鎖定,只有未實例化時才鎖,鎖完判斷是否還未實例化。線程

public static Singleton GetInstance()
{
    if (_singleton == null)
    {
        lock (syncRoot)
        {
            if (_singleton == null)
            {
                _singleton = new Singleton();
            }
        }
    }
    return _singleton;
}

3.餓漢式單例

class Singleton
{
    static readonly Singleton _instance = new Singleton();

    private Singleton() { }

    public static Singleton GetInstance()
    {            
        return _instance;
    }
}

這種靜態初始化的方式是本身被加載時就將本身實例化,所以被稱爲餓漢式單例;
而第一次被引用時纔將本身實例化的方式被稱爲懶漢式單例。C# 中餓漢式已足夠知足。code

相關文章
相關標籤/搜索