Singleton 是非常經典的 Design Pattern。最簡單,也是最具實用性的 pattern。
我也最常拿來考面試者。一個有經驗的 programmer,怎麼可能沒碰過 Singleton?
目的
全域只有一個實例(instance)
舉例
一家銀行的分行,通常會有多個櫃台(窗口),以加快處理速度。但只能有一個取號機,避免號碼牌重覆。
因此,該取號機只能有一台。如果有兩台,天下就大亂了。
程式在實作時,也常常有類似的需求。如何才能確保一個 instance呢?大家經驗累積的結果,都有類似的樣式(pattern)。這樣的 pattern 後來被取名為 singleton,以方便大家的溝通。
策略
- 將預設建構子封閉起來,不讓外界直接建立 instance.
- 使用靜態屬性(static property),讓外界取的唯一的 instance
最常見的實作如下。
實作1
class MySingleton
{
private static MySingleton _singleton = null;
private int currentNumber = 0;
private MySingleton()
{
}
public static MySingleton Current
{
get
{
if (_singleton == null)//注意這一行
_singleton = new MySingleton();
return _singleton;
}
}
public void WriteNumber()
{
currentNumber++;
Console.WriteLine(currentNumber);
}
}
Client
而Client code 的使用方式如下
MySingleton.Current.WriteNumber();
MySingleton.Current.WriteNumber();
MySingleton.Current.WriteNumber();
呼叫的結果,就是 1, 2, 3了。
實作1的方法並不完美,因為不是 thread-safe。換句話說,使用在 multi-thread 的環境下,是有可能還是建立了多個 instance。可能發生問題的程式碼,就是實作1內註解的「注意這一行」。
因此,有人改寫如下
實作2: thread-safe
class MySingleton
{
private static MySingleton _singleton = null;
private int currentNumber = 0;
private MySingleton()
{
}
public static MySingleton Current
{
get
{
if (_singleton == null)
{
lock(typeof(MySingleton))
_singleton = new MySingleton();
}
return _singleton;
}
}
public void WriteNumber()
{
currentNumber++;
Console.WriteLine(currentNumber);
}
}
實作2的方法,是使用了 lock 的 c# 語法,讓進入 lock區段的執行緒只能有一個。
實作2其實已經相當完美了。唯一美中不足的是,簡單的singleton,竟然要這麼複雜?因此,又有人改寫如下
實作3: thread-safe, eager singleton
class MySingleton
{
private static MySingleton _singleton = new MySingleton();
private int currentNumber = 0;
private MySingleton()
{
}
public static MySingleton Current
{
get
{
return _singleton;
}
}
public void WriteNumber()
{
currentNumber++;
Console.WriteLine(currentNumber);
}
}
實作3是使用了 static 的 instance,並在一開始就建立了 instance。即使用了private static MySingleton _singleton = new MySingleton();這一行。
這樣的好處,是大幅減少了程式碼的複雜度,相當容易維護。我相當推薦這一型。
實作3有一個壞處,當 Client 尚未需要 singleton的 instance時,程式就先建立了 instance。而實作2時會先判斷是否尚未建立實例。
if (_singleton == null)
因此,實作2是lazy singleton「要用時才建立instance」, 但not thread-safe。而實作3是eager singleton,「未用時已建立 instance」, thread-safe。
有沒有既是 thread-safe, 又是 lazy singleton 的寫法呢?因此產生 實作4
實作4: thread-safe, lazy singleton
class MySingleton
{
private int currentNumber = 0;
private MySingleton() { }
public static MySingleton Current
{
get {
return Inner.Current;
}
}
public void WriteNumber() {
currentNumber++;
Console.WriteLine(currentNumber);
}
class Inner {
static Inner() { }
internal static readonly MySingleton Current = new MySingleton();
}
}
實作4非常巧妙地使用 inner class 並結合 static instance 的用法,完美地完成了任務,是我目前看過最棒的 singleton 解法。
8 則留言:
範例二
if (_singleton == null)
{
lock(typeof(MySingleton))
_singleton = new MySingleton();
}
return _singleton;
應該改成
lock(typeof(MySingleton))
{
if (_singleton == null)
_singleton = new MySingleton();
}
return _singleton;
這樣吧? 否則可能會依序重複執行多次 _singleton = new MySingleton();
(因為 if 會被衝破)
沒錯。因此,比較好的會有兩層的 lock
lock(typeof(MySingleton))
{
if (_singleton == null)
lock(typeOf(MySingleton)) {
_singleton = new MySingleton();
}
}
這樣子才能防的比較正確。但可維護性又更差了。
請問為什麼要做兩層@@?
第一層不就已經 lock 住了嗎?
再呼叫一次會有差嗎?
正如你說的啊 (因為 if 會被衝破)
第一個 lock 可以防止 if 被衝破
第二個 lock 的作用是??
查一下 singleton double lock
https://msdn.microsoft.com/zh-tw/library/ff650316.aspx
應該是 double check ,使用 if 就夠了
"Double-Checked Locking is Broken"
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
那個... double check 壞掉了.... 囧
張貼留言