Set*_*gie 2 c++ singleton multithreading critical-section c++03
背景:在函数中使用局部静态变量作为单例模式的实现的一个问题是,如果多个线程同时第一次调用该函数,则可以完成静态变量的初始化两次.
我的问题是,如果你将静态变量的初始化包含在一个关键部分中,是否会阻止双重初始化的发生?例:
CRITICAL_SECTION cs;
Class get_class_instance() {
EnterCriticalSection(&cs);
// is the initialisation of c done inside the critical section?
static Class c = Class(data);
LeaveCriticalSection(&cs);
return c;
}
Run Code Online (Sandbox Code Playgroud)
或者是初始化是否神奇地完成(不是在声明/初始化时),比如在构造函数开始之前初始化变量成员?
我的问题是关于pre-C++ 11,因为根据Xeo的回答,C++ 11自己解决了这个问题.
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;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
谢谢@R.Martinho Fernandes和@Ben Voigt合作完成了最终解决方案.如果您对此过程感兴趣,请随时查看成绩单.
现在,如果您的编译器已经支持某些C++ 11功能,而不支持静态初始化功能,那么您还可以std::unique_ptr结合使用placement new和静态对齐缓冲区:
#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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3540 次 |
| 最近记录: |