singleton模式
singleton(譯為單件或單態)模式是設計模式中比較簡單而常用的模式。
有些時候在整個應用程式中,會要求某個類有且只有乙個例項,這個時候可以採用singleton模式進行設計。用singleton模式設計的類不僅能保證在應用中只有乙個例項,而且提供了一種非全域性變數的方法進行全域性訪問,稱為全域性訪問點,這樣對於沒有全域性變數概念的純物件導向語言來說是非常方便的,比如c#。
本文用乙個計數器的例子來描述在c#中如何使用singleton模式:計數的值設計為計數器類的乙個私有成員變數,它被4個不同的執行緒進行讀寫操作,為保證計數的正確性,在整個應用當中必然要求計數器類的例項是唯一的。
singleton的實現方式
首先看看教科書方式的singleton標準實現的兩種方法,以下用的是類c#偽**:
方法一:
using system;
namespace cspattern.singleton
static public singleton instance() }
} 方法二:
using system;
namespace cspattern.singleton
static public singleton instance()
return unisingleton;
} } }
singleton模式的實現有兩個技巧:一是使用靜態成員變數儲存「全域性」的例項,確保了唯一性,使用靜態的成員方法instance() 代替 new關鍵字來獲取該類的例項,達到全域性可見的效果。二是將構造方法設定成為private,如果使用new關鍵字建立類的例項,則編譯報錯,以防程式設計時候筆誤。
上面方法二的初始化方式稱為lazy initialization,是在第一次需要例項的時候才建立類的例項,與方法一中類的例項不管用不用一直都有相比,方法二更加節省系統資源。但是方法二在多執行緒應用中有時會出現多個例項化的現象。
假設這裡有2個執行緒:主線程和執行緒1,在建立類的例項的時候可能會遇到一些原因阻塞一段時間(比如網路速度或者需要等待某些正在使用的資源的釋放),此時的運**況如下:
主線程首先去呼叫instance()試圖獲得類的例項,instance()成員方法判斷該類沒有建立唯一例項,於是開始建立例項。由於一些因素,主線程不能馬上建立成功,而需要等待一些時間。此時執行緒1也去呼叫instance()試圖獲得該類的例項,因為此時例項還未被主線程成功建立,因此執行緒1又開始建立新例項。結果是兩個執行緒分別建立了兩次例項,對於計數器類來說,就會導致計數的值被重置,與singleton的初衷違背。解決這個問題的辦法是同步。
使用方法一:
using system;
using system.threading;
namespace cspattern.singleton
static public counter instance()
public void inc() //計數加1。
public int getcounter() //獲得當前計數值。 } }
以下是呼叫counter類的客戶程式,在這裡我們定義了四個執行緒同時使用計數器,每個執行緒使用4次,最後得到的正確結果應該是16:
using system;
using system.io;
using system.threading;
namespace cspattern.singleton.mutilethread
public void dosomework()
報告: 當前counter為: ", thread.currentthread.name.tostring(), mycounter.getcounter().tostring());
} }
public void clientmain() }
} 以下為main函式,本程式的測試入口:
using system;
namespace cspattern.singleton
static public void main(string args) }
} 執行結果如下:
執行緒thread 1報告: 當前counter為: 2
執行緒thread 1報告: 當前counter為: 4
執行緒thread 1報告: 當前counter為: 5
執行緒thread 1報告: 當前counter為: 6
執行緒thread 3報告: 當前counter為: 7
執行緒thread 3報告: 當前counter為: 8
執行緒thread 3報告: 當前counter為: 9
執行緒thread 3報告: 當前counter為: 10
執行緒thread 0報告: 當前counter為: 1
執行緒thread 0報告: 當前counter為: 11
執行緒thread 0報告: 當前counter為: 12
執行緒thread 0報告: 當前counter為: 13
執行緒thread 2報告: 當前counter為: 3
執行緒thread 2報告: 當前counter為: 14
執行緒thread 2報告: 當前counter為: 15
執行緒thread 2報告: 當前counter為: 16
由於系統執行緒排程的不同,每次的執行結果也不同,但是最終結果一定是16。
方法一中由於例項一開始就被建立,所以instance()方法無需再去判斷是否已經存在唯一的例項,而返回該例項,所以不會出現計數器類多次例項化的問題。
使用方法二:
using system;
using system.threading;
using system.runtime.compilerservices;
namespace cspattern.singleton
[methodimpl(methodimploptions.synchronized)] //方法的同步屬性
static public counter_lazy instance()
return unicounter;
} public void inc()
public int getcounter() } }
不知道大家有沒有注意到instance()方法上方的[methodimpl(methodimploptions.synchronized)] 語句,他就是同步的要點,他指定了instance()方法同時只能被乙個執行緒使用,這樣就避免了執行緒0呼叫instance()建立完成例項前線程1就來呼叫instance()試圖獲得該例項。
根據msdn的提示,也可以使用lock關鍵字進行執行緒的加鎖,**如下:
using system;
using system.threading;
namespace cspattern.singleton
static public counter_lazy instance()
return unicounter;
} }
public void inc()
public int getcounter()
} }
lock()是對乙個物件加互斥鎖,只允許乙個執行緒訪問其後大括號中語句塊,直到該語句塊的**執行完才解鎖,解鎖後才允許其他的執行緒執行其語句塊。
還可以使用mutex類進行同步,定義private static mutex mut = new mutex();後,修改instance()如下,同樣可以得到正確的結果:
static public counter_lazy instance()
mut.releasemutex();
return unicounter; }
注意的是,本例中使用方法二要更改方法一的客戶程式,去掉counter_lazy.intance()的注釋,並將counter.intance()注釋。
singleton模式還可以拓展,只要稍加修改,就可以限制在某個應用中只能允許m個例項存在,而且為m個例項提供全域性透明的訪問方法。
c 設計模式 Singleton模式
參考的文件 單例要求乙個類裡面只有乙個例項,並且提供了乙個全域性的訪問點。單例類裡面定義了乙個getinstance的方法,是乙個靜態方法。用於建立自己的唯一例項。在乙個系統裡面要求某個類只有乙個例項才能使用單例模式。比如印表機 或者乙個通訊口進行傳輸。using system using syst...
C 設計模式之Singleton模式
singleton是二十三個設計模式中比較重要也比較經常使用的模式。但是這個模式雖然簡單,實現起來也會有一些小坑,讓我們一起來看看吧!首先我們看看這個設計模式的uml類圖。很清晰的可以看到,有三點是需要我們在實現這個模式的時候注意的地方。其中,私有化構造器是防止外部使用者建立新的例項而靜態方法用於返...
C 設計模式之Singleton
一 功能保證乙個類僅有乙個例項。二 結構圖 三 優缺點 singleton模式是做為 全域性變數 的替代品出現的。所以它具有全域性變數的特點 全域性可見 貫穿應用程式的整個生命期,它也具有全域性變數不具備的性質 同型別的物件例項只可能有乙個。四 實現 教科書上的singleton定義如下 class...