文章目录
1. 单例模式
1.1 定义
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
稳定点:一个人只有一个实例,并且提供一个全局访问点。
变化点:可能存在多个不同类的单例。
1.2 使用场景
1.3 实现细节
//实现:
//1.静态成员确保一个类仅有一个实例,静态方法提供一个全局的访问点。
//2.构造函数和析构函数都声明为私有属性,确保全局唯一的单例不能调用构造方法创建,不能调用析构方法销毁。
//版本一:
//1. 并发下线程不安全, mutex加锁,懒汉模式----双重检测
//2. _instance是一个静态成员指针变量,new一个对象赋值给_instance,程序结束,堆上申请的内存没有被释放
//1.智能指针 2.int atexit(void (*func)(void))
class Singleton {
public:
static Singleton * GetInstance(){
//std::lock_guard<std::mutex> lock(_mutex);
//锁的粒度可以降低,只在_instance第一次创建的时候需要加锁
if(_instance == nullptr){
std::lock_guard<std::mutex> lock(_mutex);
if(_instance == nullptr) //双重检测
_instance = new Singleton();
// int atexit(void (*func)(void)) 当程序正常终止时,调用指定的函数 func。
//您可以在任何地方注册你的终止函数,但它会在程序终止的时候被调用。
atexit(Destructor);
}
return _instance;
}
private:
static void Destructor(){
if(nullptr != _instance){
delete _instance;
_instance = nullptr;
}
}
Singleton(){
}; //构造函数
~Singleton(){
}; //析构函数
Singleton(const Singleton &) = delete; //拷贝构造函数
Singleton& operator=(const Singleton&) = delete; //拷贝赋值构造函数
Singleton(Singleton &&) = delete;
Singleton& operator=(Singleton &&) = delete; //移动拷贝构造函数
static Singleton *_instance;
static std::mutex _mutex;
};
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
Singleton* Singleton::_mutex;//互斥锁初始化
上一个版本,虽然解决了线程安全问题,和堆上内存释放,但是,在多核CPU环境下会对指令的执行顺序进行优化,指令重排。
#include <mutex>
#include <atomic>
class Singleton {
public:
static Singleton * GetInstance() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存屏障
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
/*
operatoa new {}
1.分配内存
2.调用构造函数
3.返回指令
//多线程环境下 CPU 的热order操作 指令重排 1-》3-》2
*/
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存屏障
_instance.store(tmp, std::memory_order_relaxed);
atexit(Destructor);
}
}
return tmp;
}
private:
static void Destructor(){
if(nullptr != _instance){
delete _instance;
_instance = nullptr;
}
}
Singleton(){
}; //构造函数
~Singleton(){
}; //析构函数
Singleton(const Singleton &) = delete; //拷贝构造函数
Singleton& operator=(const Singleton&) = delete; //拷贝赋值构造函数
Singleton(Singleton &&) = delete;
Singleton& operator=(Singleton &&) = delete; //移动拷贝构造函数
static Singleton *_instance;
static std::mutex _mutex;
};
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
Singleton* Singleton::_mutex;//互斥锁初始化
// g++ Singleton.cpp -o singleton -std=c++11
上面的代码使用了原语操作和内存屏障,代码实现比较复杂,利用 c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束,代码实现较于容易些。
//利用C++11 magic static特性,解决了上面的所有问题
/*
1. 利用静态局部变量延时加载的特性
2. 利用局部变量特性,进程结束系统自动回收内存,自动调用析构函数
3. 静态局部变量初始化是,没有new 操作带来的 CPU指令reorder操作
4. C++11 静态局部变量初始化时,具备线程安全
*/
class Singleton{
public:
static Singleton& GetInstance(){
static Singleton instance;
return instance;
}
private:
Singleton(){
}; //构造函数
~Singleton(){
}; //析构函数
Singleton(const Singleton &) = delete; //拷贝构造函数
Singleton& operator=(const Singleton&) = delete; //拷贝赋值构造函数
Singleton(Singleton &&) = delete;
Singleton& operator=(Singleton &&) = delete; //移动拷贝构造函数
};
考虑到可能存在多个不同类的单例,使用模板编程进行拓展
template<template T>
class Singleton{
public:
static T& GetInstance(){
//这里要初始化DesingPattern ,需要调用DesingPattern 构造函数,同时会调用父类的构造函数。
static T instance;
return instance;
}
protected: //DesingPattern 子类需要调用基类class Singleton<DesingPattern>的构造函数与析构函数
Singleton(){
}; //构造函数
~Singleton(){
}; //析构函数
private:
Singleton(