单例模式
饿汉模式
类定义的时候就创建,是线程安全的
class Singleton{
private:
static Singleton* instance;
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
protected:
Singleton(){}
public:
static Singleton* getInstance() {
return instance;
}
};
Singleton* Singleton::instance = new Singleton();
懒汉模式
第一次用到的时候再创建
- 单线程
访问的时候先判断一下指针是否为空,空的话再创建,只能用在单线程,多线程是不安全的
class Singleton{
private:
Singleton();
Singleton(const Singleton&);
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
Singleton* Singleton::m_instance = nullptr;
//线程非安全版本
Singleton* Singleton::getInstance() {
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
- 多线程
1、一种方法是加锁,但是代价过高,如果对象已经创建,访问的时候其实就不需要锁了
Singleton* Singleton::getInstance() {
Lock lock; // 访问前先获取锁,函数结束会自动释放
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
也可以用pthread_mutex_t
class Singleton {
private:
static pthread_mutex_t mutex;
static Singleton * instance;
Singleton() {
pthread_mutex_init(&mutex, NULL);
}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
public:
static Singleton* getInstance() {
pthread_mutex_lock(&mutex);
if(instance == NULL) {
instance = new Singleton();
}
pthread_mutex_unlock(&mutex);
return instance;
}
};
Singleton* Singleton::instance = NULL;
pthread_mutex_t Singleton::mutex;
2、后来又提出双检查锁,但是不安全,因为new
包括三个步骤,分配内存,构造对象,返回指针,这三个步骤可能不是按顺序的,有可能先分配内存,然后就返回指针,之后才构造对象,这样当一个线程给m_instance
赋值之后,另一个线程直接就拿到m_instance
,但此时对象还没构造完成,是不能使用的
//双检查锁,但由于内存读写reorder不安全
Singleton* Singleton::getInstance() {
if(m_instance == nullptr) { // 如果对象已经创建,就不需要加锁
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
}
return m_instance;
}
3、C++11之后规定了静态局部变量的内存模型,并规定了当一个线程正在初始化一个变量的时候,其他线程必须得等到该初始化完成以后才能访问它
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
public:
static Singleton* getInstance() {
static Singleton instance;
return &instance;
}
};