前言
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
使用场景
1.程序运行过程,都是只需要一个对象的情况,如全局配置Configure
基本思路
- 构造函数声明为private或protect防止被外部函数实例化。
- 提供一个全局的静态方法(全局访问点)。
- 内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。
例1.0
#include <iostream> using namespace std; class SingletonBase { protected: SingletonBase(){ cout<<"SingletonBase created"<<endl; }; ~SingletonBase(){ cout<<"SingletonBase destoryed"<<endl; }; private: static SingletonBase* ptr; public: static SingletonBase* GetInstance(){ if (ptr== NULL){ ptr = new SingletonBase(); } return ptr; } }; SingletonBase* SingletonBase::ptr = NULL;
如果不考虑线程安全这样是可以的。
解释线程安全:
线程安全,指多线程并发调用时,对同一信号量进行操作时,存在幻读,脏读等线程问题。
例如:
线程A 调用GetInstance(),线程B
也同时调用GetInstance(),同一时刻,由于没有加锁,导致从内存读入的数据都是ptr=NULL,导致同一时间都会new一个,极大浪费了空间,且线程A,B最后操作的对象极有可能不是一个对象。
所以考虑线程安全这样是不行的。所以有了以下两种
1.懒汉式
顾名思义:懒人,第一次用到的时候才会想到去创建,跟懒加载概念类似。以时间换空间,用到的时候,会需要时间去创建实体,以减少不用的时候的堆空间。
例1.0就是最基本的懒汉式。
为了解决线程安全问题,我们选择一种方法
加锁
例1.1
#include <iostream> #include <mutex> using namespace std; /** * use lock */ class SingletonLazy1 { protected: SingletonLazy1(){ cout<<"SingletonBase created"<<endl; }; ~SingletonLazy1(){ cout<<"SingletonBase destoryed"<<endl; }; private: static SingletonLazy1* ptr; public: static mutex singleton_lock; static SingletonLazy1* GetInstance(){ singleton_lock.lock(); if (ptr== NULL){ ptr = new SingletonLazy1(); } singleton_lock.unlock(); return ptr; } }; mutex SingletonLazy1::singleton_lock; SingletonLazy1* SingletonLazy1::ptr=NULL;
同理也可以使用智能指针 lock_guard,shared_lock,unique_lock
2.饿汉式
顾名思义:饿汉,饥不择食,要吃现成的,必须类一加载,就创建,必须及时。以空间换时间,省去用到时加载的时间。
优点:
- 不需要加锁
- 提前创建好实例对象
缺点:
- 空间换时间,如果不需要实例,也会默认创建实例
例1.2
#include <iostream> #include <mutex> using namespace std; /** * hungry mode */ class SingletonHungry { protected: SingletonHungry(){ cout<<"SingletonBase created"<<endl; }; ~SingletonHungry(){ cout<<"SingletonBase destoryed"<<endl; }; private: static SingletonHungry* ptr; public: static SingletonHungry* GetInstance(){ return ptr; } }; SingletonHungry* SingletonHungry::ptr=new SingletonHungry;
关于饿汉式的线程安全问题
饿汉式内 变量线程安全我持怀疑态度
实例 多线程情况下使用单例
问题:开三个线程,使之变量A = 0,累加到100,顺序执行?
/* * * 问题:开三个线程,使之变量A = 0,累加到100,顺序执行? * */ //void ThreadFunc1(){ // while(1){ // if(SingletonBase::GetInstance()->counter >100){ // return; // } // cout<<(SingletonBase::GetInstance()->counter++)<<endl; // } //} void ThreadFunc2(){ while(1){ MyLock.lock(); if(SingletonLazy1::GetInstance()->counter >100){ MyLock.unlock(); return; } cout<<(SingletonLazy1::GetInstance()->counter++)<<endl; MyLock.unlock(); } } void ThreadFunc3(){ while(1){ MyLock.lock(); if(SingletonHungry::GetInstance()->counter >100){ MyLock.unlock(); return; } cout<<(SingletonHungry::GetInstance()->counter++)<<endl; MyLock.unlock(); } } int main() { use Base // thread th1(ThreadFunc1); // thread th2(ThreadFunc1); // thread th3(ThreadFunc1); // th1.join(); // th2.join(); // th3.join(); use Lazy // thread th1(ThreadFunc2); // thread th2(ThreadFunc2); // thread th3(ThreadFunc2); // th1.join(); // th2.join(); // th3.join(); thread th1(ThreadFunc3); thread th2(ThreadFunc3); thread th3(ThreadFunc3); th1.join(); th2.join(); th3.join(); return 0; }
补充一点:
Qt项目可以直接使用std::thread
No-Qt项目中,需要CONFIG += thread