C++ 11不需要鎖定。如果一個靜態局部變量已經被初始化,併發執行應該等待。
§6.7 [stmt.dcl] p4
如果控制進入的同時,而變量被初始化的聲明,併發執行必須等待初始化完成。
對於C++ 03,我們有這樣的:
§6.7 [stmt.dcl] p4
具有靜態存儲持續時間的所有本地對象(3.7.1)的零初始化(8.5)是在進行任何其他初始化之前執行。在第一次輸入塊之前,初始化具有用常量表達式初始化的靜態存儲持續時間的POD類型(3.9)的本地對象。允許實現在靜態存儲持續時間內執行其他本地對象的早期初始化,條件允許在允許實現靜態初始化靜態存儲持續時間在命名空間範圍(3.6.2)中的對象的情況下執行。 否則當第一次控制通過其聲明時,這樣的對象被初始化;
最後一部分很重要,因爲它適用於您的代碼。當控制首先進入get_class_instance()
時,它首先通過臨界區的初始化,然後通過單例聲明(因爲它將在臨界區內初始化它),然後將通過臨界區的去初始化。
因此從理論的角度來看,你的代碼應該是安全的。
現在,這可以改進,但不要在每個函數調用中輸入關鍵部分。 @Chethan的基本思想是健全的,所以我們將以此爲基礎。但是,我們也要避免動態分配。對於這一點,不過,我們依靠Boost.Optional:
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = Class(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
Boost.Optional避免了默認的初始化,並仔細檢查進入避免對每個函數調用的關鍵部分。然而,該版本在作業中引入了對Class
的拷貝構造函數的調用。解決方案是就地工廠:
#include <boost/utility/in_place_factory.hpp>
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = boost::in_place(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
我感謝去@R。 Martinho Fernandes和@Ben Voigt合作完成這一最終解決方案。如果您對此過程感興趣,請隨時查看transcript。現在
,如果你的編譯器支持一些C++ 11的功能了,但不是靜態初始化的東西,你也可以使用std::unique_ptr
與佈局新和靜態對齊的緩衝,結合:
#include <memory> // std::unique_ptr
#include <type_traits> // alignment stuff
template<class T>
struct destructor{
void operator(T* p) const{
if(p) // don't destruct a null pointer
p->~T();
}
};
Class& get_class_instance() {
typedef std::aligned_storage<sizeof(Class),
std::alignment_of<Class>::value>::type storage_type;
static storage_type buf;
static std::unique_ptr<Class, destructor> p;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!p)
p.reset(new (&buf[0]) Class(data));
LeaveCriticalSection(&cs);
inited = true;
}
return *p;
}
來源
2012-01-02 03:41:46
Xeo
通過編輯,我應該刪除我的答案,因爲它不再相關。 :P – Xeo 2012-01-02 03:49:36
@Xeo對不起,我應該更具體一些,但是我正在處理一個不支持C++ 11的功能的編譯器,儘管我贊成你的回答,謝謝 – 2012-01-02 03:50:31