单例模式使用的目的是 ①一个类只能初始化出一个对象 ②保证对象是线程安全的。
其做法:
1、将 构造函数 和拷贝构造函数 设置为私有 并将自身类的指针设置成静态成员,其函数方法也设置成静态方法。保证该类只创建一个对象
2、通过加锁的方式,对 对象访问加以限制
class Singleton {
private:
static Singleton * instance;
Singleton (){}
Singleton(const Singleton& other);
public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
}
以上在单线程中是可以满足一个类对象只会被初始化一次的条件。但是在多线程中 假如其中一个线程在执行 instance = new Singleton(); 语句前,而另外一个刚好进入 if 语句时,这个类就会被初始化两次。所以上述的编码方式时线程不安全的。
二、双重加锁
class Singleton {
private:
static Singleton * instance;
Singleton (){}
Singleton(const Singleton& other);
public:
static Singleton* getInstance() {
if (instance == nullptr) {
Lock lock;
if(instance == nullptr){
instance = new Singleton();
}
}
return instance;
}
}
上述代码是双重锁机制,当一个线程先获取到锁的时候,第二个线程读锁时就会等待第一个锁初始化完对象后才继续执行。但是上述写法没有考虑到内存读写reorder不安全。
正常创建对象实例会走三步骤
1) 分配对象内存
2) 调用构造器,执行初始化
3) 将对象的引用赋值给变量。
然而在编译器优化等问题下,在实际可能的运行顺序是,先执行第三步再执行第二部,即先引用给对象再调用构造器。
如果
假如其中一个线程在执行 instance = new Singleton(); 语句时,分配完了内存,将对象将对象的引用赋值给变量,此时第二个线程判断 if (instance == nullptr) 不成立直接返回了一个还未初始化完的对象。那么就会造成线程不安全。这个创建对象的二三步骤乱序执行实际上叫重排序。
在JAVA ,C#中 ,新增了一个关键字volatile,在声明成员变量时 volatile static Singleton instance; 表示禁止 instance创建对象时重排序。
而在C++ 11版本的跨平台实现
//C++ 11版本之后的跨平台实现
// atomic c++11中提供的原子操作
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;
/*
* std::atomic_thread_fence(std::memory_order_acquire);
* std::atomic_thread_fence(std::memory_order_release);
* 这两句话可以保证他们之间的语句不会发生乱序执行。
*/
Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
或者有更简洁的C++写法,但在C++11版本前不支持。
class Singleton{
public:
// 注意返回的是引用。
static Singleton& getInstance(){
static Singleton m_instance; //局部静态变量
return m_instance;
}
private:
Singleton(); //私有构造函数,不允许使用者自己生成对象
Singleton(const Singleton& other);
};