-
簡單介紹
單例模式(Singleton Pattern)是一種常見的軟件設計模式,在使用這個模式時,單例對象的類必須保證在全局中有且只有一個實例存在,並且提供了一個全局訪問的接口,這樣有利於我們協調系統整體的行爲。
-
應用場景
1.在某個服務器程序中,所以的配置數據都在一個文件中存放,這時可以統一使用一個單例對象對配置進行讀取,然後其他的模塊都可以調用該對象來對獲取當前配置信息
2.在windows下,任務管理器就是單例模式的經典應用之一,保證整個系統只能有一個Task Manager的對象。
3.許多windows應用程序存在着系統托盤,這個也是單例模式的一種應用,隱藏在右下角方便用戶進行操作。整個exe中有且只有一個托盤對象,防止程序多開導致的一系列bug,比如說已經運行當前exe,在雙擊打開程序是不會被再拉起一個登錄界面。
4.多線程的線程池對象一般是使用的單例模式,這樣方便對池衝的線程進行管理。
-
具體實現
由於目前正在做客戶端開發,所以本文在WIN32平臺下實現,順便簡單介紹一下windows防多開的實現,在windows下有許多程序在設計時是不允許用戶多開的,那麼如何實現呢?在這裏分享一個windows下的解決方案:互斥體防多開。主要原理是使用WIN32 API CreateMutexW函數
CreateMutexW( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, _In_ BOOL bInitialOwner, _In_opt_ LPCWSTR lpName);
來生成一個與當前程序相關的互斥體,當再次運行是會檢測當前互斥體是否再運行,如果是則退出程序,關於CreateMutexW函數的用法網上有許多博客介紹,這裏具體說明下lpName參數的作用,lpName主要是作爲互斥體名稱傳入參數的,那麼這個參數該如何選擇呢?這個時候可以使用uuid, uuid是一個根據標準方法生成、不依賴中央機構的註冊和分配且具有全網唯一的一個128位標識符,使用這個作爲互斥體的名稱可以做到真正的互斥。下面說說具體實現:
- 首先需要定義一個對象的結構體,用於存放文件句柄以及判斷是否在單例模式下。
- 構造函數的設計主要是使用唯一uuid作爲單例對象的互斥名稱,創建了程序的句柄,調用此函數時,可以把本地生成的uuid當作函數參數key傳入,根據GetLastError()函數返回值來確定m_inMode的值。
- 析構函數的設計就比較簡單了,可以先判斷是否在單例模式下,如果是則關閉當前文件句柄即可
struct SingletionMode::Context {
bool m_inMode;
HANDLE m_hMutex;
};
// 單例模式
struct SingletionMode {
//句柄
struct Context;
SingletionMode(const std::wstring &key);
virtual ~SingletionMode();
private:
std::shared_ptr<Context> m_context;
};
//構造函數
SingletionMode::SingletionMode(const std::wstring &key) {
m_context = std::make_shared<Context>();
m_context->m_hMutex = CreateMutexW(nullptr, FALSE, key.c_str()); //創建實例句柄
auto dwError = GetLastError();
m_context->m_inMode = (ERROR_ALREADY_EXISTS == dwError || ERROR_ACCESS_DENIED == dwError);
}
//析構函數
SingletionMode::~SingletionMode() {
closeValidHandle(m_context->m_hMutex); //這裏定義一個函數關閉實例句柄
}