桃城网站建设代理,天元建设集团有限公司六公司,公司网页设计的公司,衡水seo_衡水网站建设-燕丰收文章目录实现 1#xff1a;静态成员实现 2#xff1a;atexit 懒汉模式实现 3#xff1a;原子变量 懒汉模式实现4#xff1a;atexit 饿汉模式* 实现5#xff1a;magic static单例模式#xff1a;保证一个类仅有一个实例#xff0c;并提供一个该实例的全局访问点。
稳…
文章目录实现 1静态成员实现 2atexit 懒汉模式实现 3原子变量 懒汉模式实现4atexit 饿汉模式* 实现5magic static单例模式保证一个类仅有一个实例并提供一个该实例的全局访问点。
稳定点类只有一个实例提供全局变化点有多个类都是单例能否复用代码
实现 1静态成员
构造函数和析构函数私有化禁掉拷贝构造、拷贝赋值、移动构造、移动赋值静态成员函数静态私有成员
前两点针对唯一实例后两点针对全局访问。
问题当程序结束后不会调用析构函数堆上资源无法释放内存泄漏。虽然程序结束后堆上所有的数据被销毁但是无法保存需要持久化的数据。
class Singleton {
public:// 静态成员函数全局访问点static Singleton* getInstance() {if(nullptr _pInstance) {_pInstance new Singleton();}return _pInstance;}private:// 构造函数和析构函数私有化Singleton(); ~Singleton(); // 禁掉拷贝构造、拷贝赋值、移动构造、移动赋值Singleton(const Singleton ) delete;Singleton operator(const Singleton) delete;Singleton(Singleton ) delete;Singleton operator(Singleton ) delete;private:// 静态成员静态成员函数只能访问静态成员static Singleton* _pInstance;};// 静态成员需要初始化
Singleton* Singleton::_instance nullptr; 实现 2atexit 懒汉模式
atexit 函数在进程结束后利用回调函数自动释放堆空间。
/*
功能注册给定函数并在进程结束后调用该函数
参数函数指针指向被调用的函数(返回值、参数均为void)
*/
#include stdlib.h
int atexit(void (*function)(void));利用这一特性在进程结束时 atexit 函数调用销毁函数完成析构工作。
问题atexit 函数本身安全但是多线程环境下。存在线程安全问题。
static Singleton* getInstance() {if(nullptr _pInstance) {// 问题多个并发线程可能同时创建对象_pInstance new Singleton();atexit(Singleton::Destructor);}return _pInstance;
}为保证线程安全需要加锁。对于加锁操作只有第一次写操作创建对象的时候需要加锁其他时候都是读操作没有必要加锁。因此这里在实现的时候可以采用双重检测 double check的技巧。
#include stdlib.h
class Singleton {
public:static Singleton* getInstance() {if(nullptr _pInstance) {_pInstance new Singleton();// 线程安全双重检测double check if (nullptr _pInstance) {std::lock_guardstd::mutex lock(_mutex);if (nullptr _pInstance) {// 问题多线程环境下cpu reorder_pInstance new Singleton();atexit(Singleton::Destructor);}}return _pInstance;// 注册回调函数进程结束后调用销毁函数atexit(Singleton::Destructor);}return _pInstance;}private:Singleton(); ~Singleton(); Singleton(const Singleton ) delete;Singleton operator(const Singleton) delete;Singleton(Singleton ) delete;Singleton operator(Singleton ) delete;// 注册销毁函数为atexit的回调函数用于在进程结束后释放堆空间static void Destructor() {if (nullptr ! _instance) { delete _instance;_instance nullptr;}}private:static Singleton* _pInstance;};Singleton* Singleton::_instance nullptr; 问题new 操作符指令重排
C 98 表达单线程语义。而在多核多线程的情况下若 cpu 指令重排例如对于 new 运算符的指令执行分配内存、调用构造函数、返回指针。若发生 cpu 指令重排会优化为分配内存、返回指针却还没有调用构造函数初始化数据。此时若有其他线程访问可能造成程序的崩溃。
实现 3原子变量 懒汉模式
C 11多线程语义cpu 指令重排提供同步原语原子变量、内存屏障等
原子变量解决
原子性问题可见性问题load 可以看见其他线程最新操作的数据 store 修改数据让其他线程可见执行序问题memory_order_acuire不能重排指令memory_order_release松散指令可以重排指令。
内存屏障内存栅栏解决
可见性问题执行序问题
使用原子变量解决原子性、可见性、执行序
class Singleton {
public:static Singleton * GetInstance() {Singleton* tmp _instance.load(std::memory_order_acquire);if (tmp nullptr) {std::lock_guardstd::mutex lock(_mutex);tmp _instance.load(std::memory_order_acquire);if (tmp nullptr) {tmp new Singleton;_instance.store(tmp, memory_order_release);atexit(Destructor);}}return tmp;}
...static std::atomicSingleton* _instance;static std::mutex _mutex;
};
std::atomicSingleton* Singleton::_instance; // 静态成员需要初始化
std::mutex Singleton::_mutex; // 互斥锁初始化 改进若构造函数中存在其他原子性操作则可以使用松散的指令执行方式提升运行速度。使用内存屏障避免 tmp 指针在 new 操作未执行完就返回给用户。
原子变量解决原子性、可见性内存栅栏解决执行序
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_guardstd::mutex lock(_mutex);tmp _instance.load(std::memory_order_relaxed);if (tmp nullptr) {tmp new Singleton;// 释放内存屏障std::atomic_thread_fence(std::memory_order_release);_instance.store(tmp, std::memory_order_relaxed);atexit(Destructor);}}return tmp;}...static std::atomicSingleton* _instance;static std::mutex _mutex;
};
std::atomicSingleton* Singleton::_instance; // 静态成员需要初始化
std::mutex Singleton::_mutex; // 互斥锁初始化 问题代码复杂书写困难。
实现4atexit 饿汉模式
懒汉模式是延迟加载饿汉模式是提前加载。当系统开始运行加载类的时候就初始化类实例其他线程无法再创建实例实现线程安全。
class Singleton {
public:static Singleton* Singleton::getInstance() {if(nullptr _pInstance) {_pInstance new Singleton();atexit(Singleton::Destructor);}return _pInstance;}
...
};// 全局初始化使其在进程创建之前就不为空防止子进程创建对象
Singleton* Singleton::_instance getInstance();问题无论是否需要该类实例都必须提前创建。
* 实现5magic static
源自C effectiveC 11 magic static 特性参考官方文档静态局部变量推荐使用。
如果变量在初始化的时候并发同时进入声明语句并发线程会阻塞等待初始化结束。线程安全。静态局部变量首次经过它的声明才会被初始化在其后所有的调用中声明都会被跳过。
因此使用定义在栈上的局部静态变量保存单例对象具备所有优点
延迟加载系统自动调用析构函数回收内存没有 new 操作带来的 cpu reorder 操作线程安全
class Singleton {
public:static Singleton GetInstance() {// magic static// 定义在栈上的局部静态变量进程结束后自动释放static Singleton instance;return instance;}private:Singleton(); ~Singleton(); Singleton(const Singleton ) delete;Singleton operator(const Singleton) delete;Singleton(Singleton ) delete;Singleton operator(Singleton ) delete;
};