gpt4 book ai didi

c++ - 临界区和单例模式

转载 作者:太空狗 更新时间:2023-10-29 23:46:50 26 4
gpt4 key购买 nike

背景:在函数中使用局部静态变量作为单例模式的实现的一个问题是,如果多个线程同时第一次调用该函数,静态变量的初始化可以做两次。

我的问题是,如果将静态变量的初始化包装在临界区中,是否会阻止双重初始化的发生?示例:

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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com