设计一个类,我们只能生成该类的一个实例。
分析:
- 只能生成一个实例的类,实际上是单例模式的类型。
- 为防止用户生成新对象,将构造函数声明为私有或受保护
- 生成一个对象,可以将其用静态成员函数来记录,指向这个对象的指针声明为静态变量,存放在静态存储区,把该类的对象用new来构造放在堆中
// vs 2015
#include "stdafx.h"
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton {
private:
// 该对象指针从属于该类
static Singleton *instance;
Singleton() { cout << "generate!" << endl; };
Singleton(const Singleton &);
public:
static Singleton * GetInstance()
{
// 第一次创建时调用new;后面不再生成新对象
if (instance == nullptr)
instance = new Singleton();
return instance;
}
};
Singleton* Singleton::instance = nullptr;
int main()
{
// 第一次调用生成对象,OK
Singleton::GetInstance();
// 后面再调用时将返回第一次生成的对象
Singleton::GetInstance();
return 0;
}
程序在VS 2015中可运行。
问题:上述代码在单线程时工作正常。但在多线程下存在问题。如果两个线程同时运行到判断
if (instance == nullptr)
语句时,且instance还未被创建,则两个线程都会创建一个实例。这样就不满足单例模式的要求。
解决:多线程环境下加锁
mutex mux;
class Singleton {
// 其余代码相同
public:
static Singleton * GetInstance()
{
// 加锁,其他线程等待
mux.lock();
if (instance == nullptr)
instance = new Singleton();
// 解锁,其他线程进来发现已创建则返回
mux.unlock();
return instance;
}
};
Singleton* Singleton::instance = nullptr;
其实还有问题:每次想获取这个对象时,调用GetInstance函数都会试图上锁,造成效率不高。可以这样,由于每个时刻只有一个线程能得到锁,则每次先判断是否已创建,然后再加锁处理。
假设两个线程同时访问判断句,且此时并未创建,则两者竞争锁,有一个上锁进去创建对象然后退出。另一个得到锁进去发现已创建则退出。当后面再访问对象时,先判断是否创建,这时已创建则不会搞锁。
mutex mux;
class Singleton {
private:
static Singleton *instance;
Singleton() { cout << "generate!" << endl; };
Singleton(const Singleton &);
public:
static Singleton * GetInstance()
{
// 先判断是否已创建
// 没有再考虑上锁
if (instance == nullptr)
{
mux.lock();
if (instance == nullptr)
instance = new Singleton();
mux.unlock();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;