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