- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
背景:在函数中使用局部静态变量作为单例模式的实现的一个问题是,如果多个线程同时第一次调用该函数,静态变量的初始化可以做两次。
我的问题是,如果将静态变量的初始化包装在临界区中,是否会阻止双重初始化的发生?示例:
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;
}
或者初始化是神奇地完成的(不是在声明/初始化的时候),就像在构造函数开始之前初始化变量成员?
我的问题专门针对 C++11 之前的版本,因为根据 Xeo 的回答,C++11 会自行解决这个问题。
最佳答案
C++11 不再需要锁定。如果静态局部变量已经被初始化,并发执行将等待。
§6.7 [stmt.dcl] p4
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
对于 C++03,我们有这个:
§6.7 [stmt.dcl] p4
The zero-initialization (8.5) of all local objects with static storage duration (3.7.1) is performed before any other initialization takes place. A local object of POD type (3.9) with static storage duration initialized with constant-expressions is initialized before its block is first entered. An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that animplementation is permitted to statically initialize an object with static storage duration in namespace scope (3.6.2). Otherwise such an object is initialized the first time control passes through its declaration;
最后一部分很重要,因为它适用于您的代码。当控制第一次进入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
结合 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;
}
关于c++ - 临界区和单例模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8697207/
我最近购买了《C 编程语言》并尝试了 Ex 1-8这是代码 #include #include #include /* * */ int main() { int nl,nt,nb;
早上好!我有一个变量“var”,可能为 0。我检查该变量是否为空,如果不是,我将该变量保存在 php session 中,然后调用另一个页面。在这个新页面中,我检查我创建的 session 是否为空,
我正在努力完成 Learn Python the Hard Way ex.25,但我无法理解某些事情。这是脚本: def break_words(stuff): """this functio
我是一名优秀的程序员,十分优秀!