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

局部靜態變量初始化的線程安全問題分析

     編輯:關於C++

現象代碼

#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;
}

VC8 實際輸出

before sleep
in test
in test
in test
in test
in test
in test
in test
in test
in test
after sleep
in test

問題分析

  • 10 個線程幾乎同時調用 bar()
  • 靜態局部變量 a 的構造函數執行期間(睡眠 1 秒),其他 9 個線程未等待初始化完成,直接調用 test()
  • 甚至可能導致構造函數被多次調用(反匯編可見)

標準規定與編譯器實現

C++98/03 標準未強制要求局部靜態變量初始化的線程安全。C++11 起(§6.7/4)規定:

若控制並發進入聲明,並發執行時應等待初始化完成。若初始化拋出異常,不再嘗試初始化。

VC8 (VS2005) 尚未實現此保證,採用無鎖「雙重檢查」模式,導致競態條件。

解決方案

編譯器層面(推薦)

  • 升級到 VS2015+(完整支持 C++11 魔術靜態)
  • GCC 4.3+、Clang 2.9+、Intel ICC 13+ 均已支持

用戶代碼層面(舊編譯器)


// 方案 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;
}

最佳實踐建議

  1. 優先使用 C++11 魔術靜態(static T t;),簡潔且標準保證線程安全
  2. 避免在構造函數中執行耗時操作(如 Sleep、I/O、鎖)
  3. 若必須用舊編譯器,封裝為 call_once 或手動鎖的單例模式
  4. 嚴禁在多線程環境下依賴無鎖的局部靜態初始化
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved