单例模式:只允许创建一个类对象,实现的关键是将构造函数变为私有。
单例模式有几种实现方式:
- 懒汉模式
- 饿汉模式
- 线程安全模式(锁实现和call_once实现)
- 局部静态变量模式
1.懒汉模式
当需要使用类对象时,才去申请堆空间,避免内存浪费,这样的方式会引入两个问题,一个就是在多线程的运用场景下有可能多次申请空间去创建对象造成异常,为了解决这个问题引入了线程安全模式;另外一个就是如何释放堆空间的问题,我们在线程安全模式下再讲解。
//懒汉模式
class Singleton
{
public:
~Singleton(){cout << "调用Singleton析构函数" << endl;}
static Singleton& getInstance()
{
if (instance_mp == nullptr)
{
instance_mp = new Singleton();
}
return instance_mp;
}
void display()
{
cout << "Singleton::display()" << endl;
}
private:
Singleton(){cout << "调用Singleton构造函数" << endl;}
private:
static Singleton* instance_mp;
};
Singleton* Singleton::instance_mp = nullptr;
//饿汉模式下引入的线程安全问题,代码举例
//强调一点:在Ubuntu服务器下编译时,需要连接pthread库,并且编译flag必须要放置于命令之后,
//如:g++ -o exercise exercise.cpp -std=c++11 -lpthread
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
using namespace std;
class Singleton
{
public:
~Singleton(){}
static Singleton* getInstance()
{
if (instance_mp == nullptr)
{
instance_mp = new Singleton();
}
return instance_mp;
}
void display()
{
cout << "Singleton::display()" << endl;
}
private:
Singleton(){cout << "调用Singleton构造函数" << endl;}
private:
static Singleton* instance_mp;
};
Singleton* Singleton::instance_mp = nullptr;
void func()
{
Singleton::getInstance();
}
int main(void)
{
thread thArr[100];
for (int i=0; i<100; i++)
{
thArr[i] = thread(func);
}
for (int i=0; i<100; i++)
{
thArr[i].join();
}
cout << "主线程结束" << endl;
return 0;
}
/*输出结果:
root@chris:/home/share/cpp_prj# g++ -o exercise exercise.cpp -std=c++11 -lpthread
root@chris:/home/share/cpp_prj# ./exercise
调用Singleton构造函数
调用Singleton构造函数
调用Singleton构造函数
调用Singleton构造函数
调用Singleton构造函数
主线程结束
*/
2.饿汉模式
它会在初始化的时候就为它分配堆空间,它是一种线程安全的模式,它不需要加锁,因此执行效率高;
//饿汉模式
class Singleton
{
public:
~Singleton()
{
delete instance_mp;
instance_mp = nullptr;
}
static Singleton* getInstance()
{
return instance_mp;
}
private:
Singleton(){cout << "调用Singleton构造函数" << endl;}
private:
static Singleton* instance_mp;
};
Singleton* Singleton::instance_mp = new Singleton();
3.线程安全
线程安全模式是为了解决懒汉模式带来的问题。核心关键是添加了锁,但添加锁又引来了效率的问题,因为需要等待锁,因此又引入了双重检查机制来提高访问效率。
//线程安全模式(锁实现)
class Singleton
{
public:
~Singleton(){cout << "调用Singleton析构函数" << endl;}
static Singleton* getInstance()
{
//双重检查机制是为了在instance_mp已经不为空时能够快速返回值,而不用继续等待解锁。
if (instance_mp == nullptr)
{
//多线程场景,有可能会同时进入这个判断里面,所以添加锁,保护这段代码;
unique_lock<mutex> lock(mutex_m);
if (instance_mp == nullptr)
{
instance_mp = new Singleton();
static GarbgeCollection garbgeCollection;
}
}
return instance_mp;
}
//解决堆内存回收问题,很巧妙的用法是运用了static的生存机制;
class GarbgeCollection
{
public:
~GarbgeCollection()
{
if (Singleton::instance_mp != nullptr)
{
delete Singleton::instance_mp;
Singleton::instance_mp = nullptr;
cout << "释放instance_mp空间" << endl;
}
}
};
private:
Singleton(){cout << "调用Singleton构造函数" << endl;}
private:
static Singleton* instance_mp;
private:
static mutex mutex_m;
};
Singleton* Singleton::instance_mp = nullptr;
mutex Singleton::mutex_m;
//call_once实现
//call_once其实也是一种锁机制,它在thread库中;
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
class SingleTon
{
public:
~SingleTon()
{
cout << "SingleTon析构函数调用" << endl;
}
static SingleTon* getInstance()
{
if (instance_mp == nullptr)
{
try
{
//flag会记录回调函数被调用的状态;
call_once(flag_m, createInstance);
}
catch(const exception& e)
{
cout << "create instance_mp error, error:" << e.what() << endl;
}
}
return instance_mp;
}
static void createInstance()
{
if (instance_mp == nullptr)
{
instance_mp = new(std::nothrow) SingleTon();
static GarbgeCollection garbgeCollection;
}
if (instance_mp == nullptr)
{
throw std::exception();
}
}
class GarbgeCollection
{
public:
GarbgeCollection()
{
cout << "GarbgeCollection构造函数调用" << endl;
}
~GarbgeCollection()
{
cout << "GarbgeCollection析构函数调用" << endl;
if (SingleTon::instance_mp != nullptr)
{
delete SingleTon::instance_mp;
SingleTon::instance_mp = nullptr;
}
}
};
private:
SingleTon()
{
cout << "SingleTon构造函数调用" << endl;
}
private:
static SingleTon* instance_mp;
static once_flag flag_m;
};
SingleTon* SingleTon::instance_mp = nullptr;
once_flag SingleTon::flag_m;
void testFunc()
{
SingleTon::getInstance();
}
int main()
{
thread th[100];
for (int i=0; i<100; i++)
{
th[i] = thread(testFunc);
}
for (int i=0; i<100; i++)
{
th[i].join();
}
return 0;
}
/*
* 打印结果:
root@chris:/home/share/eclipse_workspace/exercise/src# ./exercise
SingleTon构造函数调用
GarbgeCollection构造函数调用
GarbgeCollection析构函数调用
SingleTon析构函数调用
root@chris:/home/share/eclipse_workspace/exercise/src#
* */
4.局部静态变量
它是最简单的实现方式,通过static声明类对象为全局;但是和规则“尽量不要设计变量为全局变量”相冲突;
//局部静态变量模式
class Singleton
{
public:
~Singleton(){cout << "调用Singleton析构函数" << endl;}
static Singleton& getInstance()
{
static Singleton g_instance;
return g_instance;
}
void display()
{
cout << "Singleton::display()" << endl;
}
private:
Singleton(){cout << "调用Singleton构造函数" << endl;}
};