2013-08-30 100 views
2

我有一個集合,我用互斥鎖保護。初始化後它只能被讀取,所以我不需要在那裏有一個互斥體。全局靜態初始化線程

集合已初始化並填充到全局靜態初始化程序中。我知道全局靜態初始化是在一個單獨的翻譯單元中保證的。有沒有保證全局靜態初始化是單線程的?


我有一個靜態集合,受Schwarz計數器保護,並由其他靜態對象的構造函數填充。該容器與一個互斥體相關聯。鑑於集合在main開始之後是隻讀的,如果我可以保證在單個線程中調用靜態構造函數,我希望擺脫互斥鎖。

我的理解是,靜態初始化順序通常在單個翻譯單元內定義良好,但在翻譯單元之間未指定。該標準是否允許靜態對象由不同的運行時提供的線程初始化/構建?


施瓦茨計數器:

頭文件

struct Init 
{ 
    Init(); 
    ~Init(); 
}; 
namespace 
{ 
    Init _init; 
} 
extern std::map<int, std::unique_ptr<...>> &protected; 

源文件:

namespace 
{ 
    int init_count; 
    std::aligned_storage<sizeof(std::map<int, std::unique_ptr<...>>), alignof(std::map<int, std::unique_ptr<...>>>)> protected_storage; 
} 
std::map<int, std::uniqe_ptr<...>> &protected = *reinterpret_cast<std::map<int, std::unique_ptr<...>> *>(&protected_storage); 
Init::Init() 
{ 
    if (!init_counter++) 
    { 
     new(&protected_storage) std::map<int, std::unique_ptr<...>>(); 
    } 
} 
Init::~Init() 
{ 
    if (!--init_counter) 
    { 
     protected.~std::map<int, std::unique_ptr<...>>(); 
    } 
} 

收藏人羣:

struct helper 
{ 
    helper(...) 
    { 
     protected.insert(std::make_pair(...)); 
    } 
}; 

展開的宏將創建幫助器的靜態實例。

+1

在C++ 11中,靜態初始化不會引入數據競爭。 –

+0

靜態初始化是*零初始化*或*常量初始化*。我認爲你不可能在多線程和單線程* static * init(非本地變量)之間有明顯的差異。 – dyp

+0

我們真的在談論全局靜態初始化器,還是我們也在考慮靜態對象的構造器? – jxh

回答

3

是否有任何保證全局靜態初始化將單線程?

您的意思是動態初始化。不,單線程初始化顯然不能保證。

從3.6.2:

如果程序啓動一個線程(30.3),隨後的初始化的變量的 是未測序相對於在不同的翻譯 單元中定義的變量的初始化。否則,相對於在不同翻譯單元中定義的變量的初始化 ,變量的初始化被不確定地排序。如果一個程序啓動一個線程,變量的後續無序初始化相對於其他每個動態初始化都是沒有序列化的。否則, 變量的無序初始化不定對於如果您在程序中從兩個不同的TU啓動一個線程,然後兩個不同的全局變量在理論上有其構造函數運行測序以其他每一個動態 初始化

所以同時從兩個不同的線程。

處理這些問題的最好辦法是來包裝你的靜態存儲持續時間的變量爲局部靜態變量在下面的「單模式」:

const T& f() 
{ 
    static T t(a,b,c); 
    return t; 
} 

最新的標準保證的t施工線程 - 安全,所以你根本不需要互斥鎖(至少沒有明確指定,編譯器會爲你生成守護進程)。

作爲一個額外的好處,該對象是在第一次調用f時「懶惰地」構建的,因此您不必擔心初始化順序。如果多個這樣的單例在構造函數中互相調用(當然,依賴關係是非循環的),它們將按照工作順序進行初始化。非局部變量不是這種情況。