单例模式
确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例
- 只能有一个实例;(禁止拷贝和赋值,静态的实例对象)
- 必须自己创建这个实例;(私有化构造函数)
- 必须自己向整个系统提供这个实例。(静态的方法调用构造函数)
- 多线程使用双重检查确保线程安全。
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
#include <iostream>
#include <string.h>
#include <mutex>
using namespace std;
class Singleton
{
public:
static Singleton* getInstance();
private:
Singleton(){}
Singleton(const Singleton& other) {}
Singleton& operator=(const Singleton& other) {}
static Singleton* m_instance;
static std::mutex m_mutex;
};
Singleton* Singleton::m_instance= nullptr;
std::mutex Singleton::m_mutex;
Singleton* Singleton::getInstance(){
if (m_instance== nullptr){//双重检查
std::lock_guard<std::mutex> lock(m_mutex);
if (m_instance== nullptr){
m_instance= new Singleton();
}
}
return m_instance;
}
#endif //__SINGLETON_H__
由于new的动作并不是原子操作,所以在多线程中,一般的单例模式会出现reorder的情况,程序会发生意想不到的错误;
编译器会对代码进行优化,编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令
若有两个线程A、B,当线程A执行到instance = new Singleton()时,由于发生了reorder,可能先完成了步骤1和步骤3,还未执行步骤2,此时m_instance已经不是NULL,但是并未完成构造;此时线程B调用getInstance()函数时,判断m_instance并不是nullptr,直接返回线程A为构造完成的m_instace,这样线程就不是安全的,会造成程序的崩溃。
下面时c++11的解决方案:
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
#include <iostream>
#include <string.h>
#include <mutex>
using namespace std;
//方案1
class Singleton
{
public:
static Singleton* getInstance();
virtual ~Singleton() {}
private:
Singleton() {}
Singleton(const Singleton& other) {}
Singleton& operator=(const Singleton& other) {}
static std::atomic<Singleton*> m_instance;
static std::mutex m_mutex;
};
// 线程安全的双检查锁 -- C++ 11版本之后的跨平台实现
std::atomic<Singleton*> Singleton::m_instance = nullptr;
std::mutex Singleton::m_mutex;
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;
}
//方案2,使用关键字volatile,不是跨平台
class Singleton2
{
public:
volatile static Singleton2* getInstance();
virtual ~Singleton2() {}
private:
Singleton2() {}
Singleton2(const Singleton& other) {}
Singleton2& operator=(const Singleton2& other) {}
volatile static Singleton2* m_instance;
static std::mutex m_mutex;
};
std::mutex Singleton2::m_mutex;
volatile Singleton2* Singleton2::m_instance = nullptr;
volatile Singleton2* Singleton2::getInstance() {
if (m_instance == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_instance == nullptr) {
m_instance = new Singleton2();
}
}
return m_instance;
}
#endif //__SINGLETON_H__