單例模式:確保一個類只有一個實例,並提供一個全局訪問點。(定義)
概念拆解:
(1)確保一個類只有一個實例
(2)提供一個訪問它的全局訪問點
個人理解:
一個類不被new,在類里的方法不被重復的new,在多線程調用實例時,確保只有一個實例在運行。
生活中的例子:
一個國家只有一個總統。
簡單的單例模式代碼:
/// <summary> /// 單例模式的實現 /// </summary> public class Singleton { // 定義一個靜態變量來保存類的實例 private static Singleton uniqueInstance; // 定義私有構造函數,使外界不能創建該類實例 private Singleton() { } /// <summary> /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點 /// </summary> /// <returns></returns> public static Singleton GetInstance() { // 如果類的實例不存在則創建,否則直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
》》在多線程中,需要確保一個實例。(我們可以使用線程鎖lock來控制 )
/// <summary> /// 單例模式的實現 /// </summary> public class Singleton { // 定義一個靜態變量來保存類的實例 private static Singleton uniqueInstance; // 定義一個標識確保線程同步 private static readonly object locker = new object(); // 定義私有構造函數,使外界不能創建該類實例 private Singleton() { } /// <summary> /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點 /// </summary> /// <returns></returns> public static Singleton GetInstance() { // 當第一個線程運行到這里時,此時會對locker對象 "加鎖", // 當第二個線程運行該方法時,首先檢測到locker對象為"加鎖"狀態,該線程就會掛起等待第一個線程解鎖 // lock語句運行完之后(即線程運行完之后)會對該對象"解鎖" lock (locker) { // 如果類的實例不存在則創建,否則直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } return uniqueInstance; } }
》》多線程的“雙重鎖定”(目的:為了減少不必要的開銷)
/// <summary> /// 單例模式的實現 /// </summary> public class Singleton { // 定義一個靜態變量來保存類的實例 private static Singleton uniqueInstance; // 定義一個標識確保線程同步 private static readonly object locker = new object(); // 定義私有構造函數,使外界不能創建該類實例 private Singleton() { } /// <summary> /// 定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點 /// </summary> /// <returns></returns> public static Singleton GetInstance() { // 當第一個線程運行到這里時,此時會對locker對象 "加鎖", // 當第二個線程運行該方法時,首先檢測到locker對象為"加鎖"狀態,該線程就會掛起等待第一個線程解鎖 // lock語句運行完之后(即線程運行完之后)會對該對象"解鎖" // 雙重鎖定只需要一句判斷就可以了 if (uniqueInstance == null) { lock (locker) { // 如果類的實例不存在則創建,否則直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
簡單模擬網站計數功能
public class Singleton { private static Singleton instance;//靜態實例 private static readonly object locker = new object();//靜態鎖 public int count = 1; private Singleton() {//構造函數 while (true) { Console.ReadKey(); count += 1; Console.WriteLine(count.ToString()); } } public static Singleton GetInstance()//方法,方法中去實例化類. { if (instance == null) { lock(locker) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
在某個時候我只需要一個線程去處理事務,不想有多個實例時。例如我們建立一個數據庫存取的管理類。類中有許多的方法。
public class DBManager { private static DBManager uniqueInstance;//定義一個靜態變量來保存類的實例 private static readonly object locker = new object();//定義一個線程鎖 private static SQLiteHelp sqliteHelper;//實例化一個數據庫連接 #region 全局訪問點 private DBManager() { string dbPath = Application.StartupPath + "\\Data.thl"; sqliteHelper = new SQLiteHelp("data source=" + dbPath + ";Pooling=true;FailIfMissing=false"); } /// <summary> /// 全局訪問點 /// </summary> /// <returns></returns> public static DBManager GetInstance() { if (uniqueInstance == null) { lock (locker) { if (uniqueInstance == null) { uniqueInstance = new DBManager(); } } } return uniqueInstance; } #endregion #region 文件列表操作 /// <summary> /// 插入路徑 /// </summary> /// <param name="path">路徑</param> /// <returns></returns> public bool InsertFilePath(string path) { string sql = "insert into RunInfoList (FilePath,Status) values ('" + path + "','停止')"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 插入路徑 /// </summary> /// <param name="serverId">游服ID</param> /// <param name="path">路徑</param> /// <returns></returns> public bool InsertFilePath(int serverId,string path) { string sql = "insert into RunInfoList (ServerId,FilePath,Status) values ('"+serverId+"','" + path + "','停止')"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 刪除 /// </summary> /// <param name="path">路徑</param> /// <returns></returns> public bool DeleteFilePath(string path) { string sql = "delete from RunInfoList where FilePath = '" + path + "'"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 更新最后的備份時間 /// </summary> /// <param name="path">路徑</param> /// <returns></returns> public bool UpdateTime(string path) { string sql = "update RunInfoList set LastBackupTime=datetime('now') where FilePath = '" + path + "'"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } /// <summary> /// 獲取文件列表 /// </summary> /// <returns></returns> public List<FileListInfo> GetFileList() { string sql = "select * from RunInfoList"; var reader = sqliteHelper.ReturnDataReader(sql); List<FileListInfo> fileList = new List<FileListInfo>(); while (reader.Read()) { FileListInfo data = new FileListInfo(); if (!Convert.IsDBNull(reader["ServerId"])) { data.ServerId = Convert.ToInt32(reader["ServerId"]); } if (!Convert.IsDBNull(reader["FilePath"])) { data.FilePath = reader["FilePath"].ToString(); } if (!Convert.IsDBNull(reader["Status"])) { data.Status = reader["Status"].ToString(); } if (!Convert.IsDBNull(reader["LastBackupTime"])) { data.LastBackTime = reader["LastBackupTime"].ToString(); } fileList.Add(data); } return fileList; } /// <summary> /// 更新ServerId /// </summary> /// <param name="path"></param> /// <returns></returns> public bool UpdateServerId(string path,string serverid) { string sql = "update RunInfoList set ServerId='"+serverid+"' where FilePath = '" + path + "'"; var affectedCount = sqliteHelper.ExecuteNonQuery(sql); if (affectedCount == 1) { return true; } else { return false; } } #endregion }
說了單例模式的一些概念和代碼,重要的是我們要怎么應用在實際的開發中?以下是我在博客園找到的關於單例的運用場景。
1. Windows的Task Manager(
任務管理器)就是很典型的單例模式(這個很熟悉吧),想想看,是不是呢,你能打開兩個windows task manager嗎? 不信你自己試試看哦~
2. windows的Recycle Bin(
回收站)也是典型的單例應用。在整個系統運行過程中,回收站一直維護着僅有的一個實例。
3.
網站的計數器,一般也是采用單例模式實現,否則難以同步。
4.
應用程序的日志應用,一般都何用單例模式實現,這一般是由於共享的日志文件一直處於打開狀態,因為只能有一個實例去操作,否則內容不好追加。
5.
Web應用的配置對象的讀取,一般也應用單例模式,這個是由於配置文件是共享的資源。
6.
數據庫連接池的設計一般也是采用單例模式,因為數據庫連接是一種數據庫資源。數據庫軟件系統中使用數據庫連接池,主要是節省打開或者關閉數據庫連接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因為何用單例模式來維護,就可以大大降低這種損耗。
7.
多線程的線程池的設計一般也是采用單例模式,這是由於線程池要方便對池中的線程進行控制。
8.
操作系統的文件系統,也是大的單例模式實現的具體例子,一個操作系統只能有一個文件系統。
9.
HttpApplication 也是單位例的典型應用。熟悉ASP.NET(IIS)的整個請求生命周期的人應該知道HttpApplication也是單例模式,所有的HttpModule都共享一個HttpApplication實例.
總結以上,不難看出:
單例模式應用的場景一般發現在以下條件下:
(1)資源共享的情況下,避免由於資源操作時導致的性能或損耗等。如上述中的日志文件,應用配置。
(2)控制資源的情況下,方便資源之間的互相通信。如線程池等。
參考資料:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html