多線程環境下,GetInstance() 若無同步,可能創建多個實例。經典雙重檢查鎖(DCLP)在 C/C++ 中因內存模型和編譯器優化(指令重排)而不安全,除非配合內存屏障或 atomic。
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;
用原子操作檢查標誌,避免每次加鎖;首次進入時加關鍵區二次確認。
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;
標準庫提供 std::once_flag 和 std::call_once,類型安全、跨平台、無需手動管理鎖。
#includeclass 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 保證局部靜態變數初始化線程安全(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;
};
+ (instancetype)sharedInstance {
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; });
return instance;
}
輸出顯示 init_instance() 只執行一次(Sleep 3 秒僅出現一次),證明線程安全。
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| pthread_once | POSIX 系統、舊代碼庫 | 標準、可靠 | 需鏈接 pthread |
| Win32 Interlocked+CS | Windows 專用、高性能 | 無鎖快速路徑 | 平台綁定、代碼較繁瑣 |
| std::call_once | C++11+ 跨平台 | 標準、類型安全 | 需 C++11 支持 |
| 局部靜態變數 | C++11+ 通用首選 | 最簡潔、無鎖讀取 | 析構順序不可控、難以手動銷毀 |
推薦:現代 C++ 專案優先使用局部靜態變數;需顯式控制生命週期或兼容舊標準時用 std::call_once。