首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

併發編程:C/C++ 線程安全單例模式實現詳解

     編輯:關於C++

單例模式的線程安全挑戰

多線程環境下,GetInstance() 若無同步,可能創建多個實例。經典雙重檢查鎖(DCLP)在 C/C++ 中因內存模型和編譯器優化(指令重排)而不安全,除非配合內存屏障或 atomic。

方案一:pthread_once (POSIX)

pthread_once_t 保證初始化函數只被調用一次,內部實現包含互斥鎖與屏障,跨平台可靠。

class Singleton {
    static pthread_once_t once;
    static Singleton* instance;
    static void init() { instance = new Singleton(); }
    Singleton() {}
public:
    static Singleton* GetInstance() {
        pthread_once(&once, init);
        return instance;
    }
};
pthread_once_t Singleton::once = PTHREAD_ONCE_INIT;
Singleton* Singleton::instance = nullptr;

方案二:Windows InterlockedExchange + Critical Section

用原子操作檢查標誌,避免每次加鎖;首次進入時加關鍵區二次確認。

class Singleton {
    static LONG done;           // 0=未初始化, 1=已完成
    static CRITICAL_SECTION cs;
    static Singleton* instance;
    static void init() { instance = new Singleton(); }
    Singleton() {}
public:
    static Singleton* GetInstance() {
        if (!InterlockedExchangeAdd(&done, 0)) { // 原子讀取
            EnterCriticalSection(&cs);
            if (!done) {
                init();
                done = 1;
            }
            LeaveCriticalSection(&cs);
        }
        return instance;
    }
};
LONG Singleton::done = 0;
CRITICAL_SECTION Singleton::cs = {0}; // 需在進程啟動時 InitializeCriticalSection
Singleton* Singleton::instance = nullptr;

方案三:C++11 std::call_once (現代標準)

標準庫提供 std::once_flag 和 std::call_once,類型安全、跨平台、無需手動管理鎖。

#include 
class Singleton {
    static std::once_flag flag;
    static Singleton* instance;
    static void init() { instance = new Singleton(); }
    Singleton() {}
public:
    static Singleton* GetInstance() {
        std::call_once(flag, init);
        return instance;
    }
};
std::once_flag Singleton::flag;
Singleton* Singleton::instance = nullptr;

方案四:C++11 局部靜態變數 (最簡潔、推薦)

C++11 保證局部靜態變數初始化線程安全(magic statics),編譯器自動生成同步代碼。

class Singleton {
    Singleton() {}
public:
    static Singleton& GetInstance() {
        static Singleton instance; // C++11 線程安全初始化
        return instance;
    }
    // 禁止拷貝/移動
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

Objective-C: dispatch_once

+ (instancetype)sharedInstance {
    static id instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; });
    return instance;
}

測試驗證(併發 100 線程各調用 10000 次)

輸出顯示 init_instance() 只執行一次(Sleep 3 秒僅出現一次),證明線程安全。

總結與建議

方案適用場景優點缺點
pthread_oncePOSIX 系統、舊代碼庫標準、可靠需鏈接 pthread
Win32 Interlocked+CSWindows 專用、高性能無鎖快速路徑平台綁定、代碼較繁瑣
std::call_onceC++11+ 跨平台標準、類型安全需 C++11 支持
局部靜態變數C++11+ 通用首選最簡潔、無鎖讀取析構順序不可控、難以手動銷毀

推薦:現代 C++ 專案優先使用局部靜態變數;需顯式控制生命週期或兼容舊標準時用 std::call_once。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved