单例模式的实现方法很多。要考虑:线程安全,生命周期,资源释放等问题会涉及到整个编程体系。
单例模式singleton
普通的简单的单例模式实现:
class Singleton
{
public:
static Singleton& instance()
{
if(m_pinstance== 0)
{
//很巧两个线程同时进入该区域,就会创建2次堆对象,
//并且第一次new的堆对象再也控制不住了,要对_instance变量加上互斥锁:
m_pinstance= new Singleton();
}
return *m_pinstance;
}
private:
Singleton(){};
~Singleton(){};
Singleton(const Singleton&){};
Singleton& operator=(const Singleton&){};
private:
static Singleton* m_pinstance;
};
Singleton* Singleton::m_pinstance= 0;
上面的单例模式直到Instance()首次被调用,才会生成唯一实例,这是被称为延迟初始化(Lazy initialization)的设计逻辑,避免程序在初始化时加载过重,在启动初始化消耗较大的程序里,这种设计思想有很大优势。
懒加载模式Lazy Singleton不是线程安全的单例模式。
注意,Instance()的返回值是实例的引用而不是指针,为的是保险防止使用者delete唯一实例。
线程安全的单例模式
简单的加上一个锁,解决了线程安全问题,但是引起效率低。加锁是比较慢的。
Singleton* Singleton::instance() {
Lock lock; //每次使用都加锁判断,锁的频繁,资源浪费,性能低
if (_instance == 0) {
_instance = new Singleton;
}
return _instance;
}
只有第一次调用该函数,在堆内存创建实例时才需要加锁 。
双检查锁
用双检测锁模式(Double-Checked Locking Pattern)解决线程安全问题。在java社区内相关文章很多。
Singleton* Singleton::instance() {
if (_instance == 0) { // 1st check //解决频繁上锁,有实例后不再加锁
Lock lock;
if (_instance == 0) { // 2nd check
_instance = new Singleton; //这句有三个步骤,仍然有可能多个线程进来
}
}
return _instance;
}
由于编译器的优化、处理器的优化,可能会导致指令重排,指令顺序优化排列。执行结果不变。
_instance = new singleton() 这句代码有三个步骤:
1.按对象大小在堆中开辟分配空间 operator new(sizeof(Singleton)); // Step 1
2.在分配的空间中构造对象初始化 new (_instance) Singleton; // Step 2
3.使_instance指向分配的内存地址 _instance = 内存对象 // Step 3
编译器的执行顺序可能是123 或者132 ,就会造成多个线程同时进入临界区执行其中,多new一个对象
使用volatile关键字的代码,也不能保证正常工作在多线程环境中。
双检查锁缺点:如指令重排、多核处理器等问题,让双检查锁实现起来比较复杂。
饿汉模式 Eager Singleton
在程序开始的时就完成了实例的创建。静态属性instance在main函数之前初始化,所以没有线程安全的问题。
潜在问题:在于no-local static对象(函数外的static对象)在不同编译单元(可理解为cpp文件和其包含的头文件)中的初始化顺序是未定义的。如果在初始化完成之前,调用 Instance()方法,会返回一个未定义的实例。
class Singleton
{
public:
static Singleton& Instance()
{
return instance;
}
private:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
private:
static Singleton instance;
}
Singleton Singleton::instance;
局部静态变量 Singleton
c++11标准做法:使用local static对象作为唯一实例。当第一次调用Instance()时才创建实例。
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton instance;
return instance;
}
private:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};