#include <stdio.h>
#include <process.h>
#include <windows.h>
class foo {
public:
foo() {
printf("before sleep\n");
Sleep(1000);
printf("after sleep\n");
}
void test() { printf("in test\n"); }
};
foo* bar() {
static foo a;
return &a;
}
unsigned __stdcall thread(void*) {
foo* p = bar();
p->test();
return 0;
}
int main() {
for (int i = 0; i < 10; ++i) {
uintptr_t t = _beginthreadex(NULL, 0, thread, NULL, 0, NULL);
CloseHandle((HANDLE)t);
}
Sleep(5000);
return 0;
}
before sleep
in test
in test
in test
in test
in test
in test
in test
in test
in test
after sleep
in test
bar()a 的構造函數執行期間(睡眠 1 秒),其他 9 個線程未等待初始化完成,直接調用 test()C++98/03 標準未強制要求局部靜態變量初始化的線程安全。C++11 起(§6.7/4)規定:
若控制並發進入聲明,並發執行時應等待初始化完成。若初始化拋出異常,不再嘗試初始化。
VC8 (VS2005) 尚未實現此保證,採用無鎖「雙重檢查」模式,導致競態條件。
// 方案 1:全局/命名空間靜態對象(程序啟動時單線程初始化)
foo& get_foo() {
static foo instance; // C++11 保證線程安全
return instance;
}
// 方案 2:call_once (C++11)
#include <mutex>
foo* bar() {
static foo* ptr = nullptr;
static std::once_flag flag;
std::call_once(flag, []{ ptr = new foo(); });
return ptr;
}
// 方案 3:手動雙重檢查鎖 (C++98,需 volatile/原子操作)
foo* bar() {
static foo* ptr = nullptr;
static CRITICAL_SECTION cs;
static BOOL init = FALSE;
if (!init) {
EnterCriticalSection(&cs);
if (!init) {
ptr = new foo();
init = TRUE;
}
LeaveCriticalSection(&cs);
}
return ptr;
}
static T t;),簡潔且標準保證線程安全Sleep、I/O、鎖)call_once 或手動鎖的單例模式