單例模式 Windows下防止多開簡介

  • 簡單介紹

單例模式(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位標識符,使用這個作爲互斥體的名稱可以做到真正的互斥。下面說說具體實現:

  1. 首先需要定義一個對象的結構體,用於存放文件句柄以及判斷是否在單例模式下。
  2. 構造函數的設計主要是使用唯一uuid作爲單例對象的互斥名稱,創建了程序的句柄,調用此函數時,可以把本地生成的uuid當作函數參數key傳入,根據GetLastError()函數返回值來確定m_inMode的值。
  3. 析構函數的設計就比較簡單了,可以先判斷是否在單例模式下,如果是則關閉當前文件句柄即可
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);    //這裏定義一個函數關閉實例句柄
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章