单例模式:是非常典型常用的一种设计模式(大佬们针对典型场景设计的解决方案)
单例模式的特点:
某些类,只应该具有一个对象(实例),就称之为单例。
例如一个男人只能有一个媳妇。
在很多服务器开发场景中,经常需要让服务器加载很多的数据(上百G)到内存中,此时往往要用一个单例的类来管理这些数据。
饿汉方式和懒汉方式
- 吃完饭立刻洗碗,这就是饿汉方式。因为下一顿吃的时候可以立刻拿着碗就能吃饭;
- 吃完饭,先把碗放下,然后下一顿饭用到这个碗了再洗碗,这就是懒汉方式
饿汉方式的实现:
使用static就可以—将一个成员变量设置为静态变量,则所有对象共用一份资源,并且在程序初始化时就会申请资源(不涉及线程安全)。
代码实现:
template <typename T>
class Singleton{
static T data;
public:
static T* GetInstance(){
return &data;
}
};
只要通过 Singleton这个包装类来使用T对象,则一个进程中只有一个T对象的实例。
懒汉方式的实现:
资源在使用的时候发现还没有加载,则申请加载。程序初始化比较快,第一次运行某个模块的时候就会比较慢,因为这时候去加载相应资源。
代码实现:
#include <cstdio>
#include <mutex>
#include <thread>
std::mutex g_mutex;
class single_instance{
public:
volatile static int *get_instance(){
// 不管—data资源是否已经被加载, 每次都要加锁判断, 然后解锁
// 若资源已经加载过了, 加锁解锁就有点浪费资源, 并且容易造成锁冲突
// 4. 二次判断, 避免每次都会加锁解锁
if(_data == NULL){
//-----------------//
g_mutex.lock();// 3.实现线程安全
if(_data == NULL){
_data = new int;
*_data = 10;
}
g_mutex.unlock();
}
return _data;
}
private:
//1.static 保证所有对象使用同一份资源
//2.volatile 防止编译器过度优化
volatile static int *_data;
};
volatile int *single_instance::_data = NULL;
实现所注意的细节:
- 使用static保证所有对象使用同一份资源;
- 使用volatile,防止编译器过度优化
- 实现线程安全,保证资源判断以及申请过程是安全的
- 外部二次判断,以及避免资源已经加载成功每次获取都要加锁解锁,以及所带来的锁冲突。