前言
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
使用场景
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