再谈单例模式

原文链接:再谈单例模式
发布时间:2015-11-17 17:04:35

之前写过一篇博客 《C++单例模式的模板基类》 http://blog.kentonly.com/2015/09/04/2015-09-04-C++单例模式的模板基类/

但是,最近才发现实际 上 static T* Instance() 的实现是有一点bug 的,下面分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static T *Instance()
{
if(NULL != m_Instance)
{
return m_Instance;
}
m_Mutex.Lock();
if(NULL != m_Instance)
{
m_Mutex.UnLock();
return m_Instance;
}
m_Instance = new(std::nothrow) T();
m_Mutex.UnLock();
return m_Instance;
}

这种实现在多线程环境下,可能会有bug ,原因就在 m_Instance = new (std::nothrow) T() 这行代码。

这句代码实际上分三步执行:

1、分配内存

2、调用构造函数,初始化内存空间

3、将内存地址赋值给单例指针

代码可转化为如下形式:

1
2
3
4
5
T*  p  =  newMemory(sizeof(T))

p->BaseSingleton::BaseSingleton()

m_Instance = p

编译器出于优化的目的,可能会将第一步和第三步合并,变成如下:

1
2
3
m_Instance  =  newMemory(sizeof(T))

m_Instance->BaseSingleton::BaseSingleton()

那么在执完第一行代码时,有可能会切换到其他线程,其他线程如果调用 Instance()函数,由于m_Insatnce已经赋值,将返回这个指针,但是

指针指向的内存并未初始化,所以会造成不可预知的错误。

在Boost库中,有一种很好的单例实现模式,我做了一点修改,就是引用改成指针。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#ifndef BASESINGLETON_H
#define BASESINGLETON_H

/**
单例模式采用boost源码
由于boost单例返回的是引用,下面改成指针
*/
template<class T>
class BaseSingleton
{
public:
BaseSingleton(){}
private:
struct object_creator
{
// This constructor does nothing more than ensure that instance()
// is called before main() begins, thus creating the static
// T object before multithreading race issues can come up.
object_creator() { BaseSingleton<T>::Instance(); }
inline void do_nothing() const { }
};
static object_creator create_object;

//BaseSingleton();

public:
typedef T object_type;

// If, at any point (in user code), BaseSingleton<T>::instance()
// is called, then the following function is instantiated.
static object_type* Instance()
{
// This is the object that we return a reference to.
// It is guaranteed to be created before main() begins because of
// the next line.
static object_type obj;

// The following line does nothing else than force the instantiation
// of BaseSingleton<T>::create_object, whose constructor is
// called before main() begins.
create_object.do_nothing();

return &obj;
}
};
template <class T>
typename BaseSingleton<T>::object_creator
BaseSingleton<T>::create_object;

#endif // BASESINGLETON_H

我之前实现的单例模式中,必须是第一次手动调用Instance()才生成单例对象,这样就引入了多线程的问题。

而Boost实现方式,在单例类中定义一个内嵌的静态的结构体,这个结构体生成时调用自己的构造函数,构造函数中执行Instance()函数,由于单例类内的静态的结构体生成时,是在main()执行之前,所以巧妙地绕开了多线程的问题。