4个线程安全的单例模式
1、概述
单例模式是程序生命周期内,该类只有一个实例,并提供一个该实例访问点,适用于全局配置类、资源管理类等场景。
单实例模式特点:
1、构造函数私有
2、拷贝构造和赋值构造函数为私有
3、类有一个静态方法获取实例
2、静态局部变量(使用最广泛)
C++11标准中,要求局部静态变量初始化具有线程安全性
特性:如果变量在初始化时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束
class Singleton
{
public:
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton(){}
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};
3、std::call_once方案
std::call_once是C++11引入的新特性,需要包含头文件<mutex>,确保函数在多线程环境下,只执行一次。
class Singleton {
public:
~Singleton()
{
instance.reset();
}
static Singleton* getInstance() {
std::call_once(single_flag, []() {
instance.reset(new Singleton());
});
return instance.get();
}
Singleton(const Singleton&) = delete;
void operator=(const Singleton&) = delete;
private:
Singleton() {};
static std::once_flag single_flag;
static std::unique_ptr<Singleton> instance;
};
std::once_flag Singleton::single_flag;
std::unique_ptr<Singleton> Singleton::instance;
4、双检索方案(用的少)
// 加锁的懒汉式实现
class Singleton
{
public:
// 获取单实例对象
static Singleton *GetInstance();
//释放单实例,进程退出时调用
static void deleteInstance();
// 打印实例地址
void Print();
private:
// 将其构造和析构成为私有的, 禁止外部构造和析构
Singleton();
~Singleton();
// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
Singleton(const Singleton &signal);
const Singleton &operator=(const Singleton &signal);
private:
// 唯一单实例对象指针
static Singleton *m_SingleInstance;
static std::mutex m_Mutex;
};
//初始化静态成员变量
Singleton *Singleton::m_SingleInstance = nullptr;
std::mutex Singleton::m_Mutex;
Singleton * Singleton::GetInstance()
{
// 这里使用了两个 if 判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,
// 避免每次调用 GetInstance的方法都加锁,锁的开销毕竟还是有点大的。
if (m_SingleInstance == nullptr)
{
std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
if (m_SingleInstance == nullptr)
{
volatile auto temp = new (std::nothrow) Singleton();
m_SingleInstance = temp;
}
}
return m_SingleInstance;
}
void Singleton::deleteInstance()
{
std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
if (m_SingleInstance)
{
delete m_SingleInstance;
m_SingleInstance = nullptr;
}
}
void Singleton::Print()
{
std::cout << "我的实例内存地址是:" << this << std::endl;
}
Singleton::Singleton()
{
std::cout << "构造函数" << std::endl;
}
Singleton::~Singleton()
{
std::cout << "析构函数" << std::endl;
}
5、饿汉模式
该模式程序启动时,就将类实例化了。
这种模式有个注意事项:如果业务中有多个饿汉类,要考虑全局初始化顺序和依赖关系。
class Singleton {
private:
static Singleton* instance; // 单例对象指针
Singleton() {} // 私有构造函数
public:
static Singleton* GetInstance() {
return instance; // 提供一个方法获取单例对象
}
};
// 在类外初始化静态成员
Singleton* Singleton::instance = new Singleton();
学习链接:https://github.com/0voice