C++单例模式的实现
很多场景下都可以用到单例模式作为设计模式,比如:线程池类,服务器类(待实现挖坑),内存池类(待实现挖坑),
单例模式的两种实现方式:饿汉模式和懒汉模式
饿汉模式的缺点如下:
1. 由于编译时需要初始化静态变量,导致程序启动变慢
2. 如果有两个单例模式设计的类,而且其中一个依赖另一个,但是编译器并不确定哪个类的先初始化,此时有可能导致编译错误
饿汉模式
在编译阶段就把单例创建好,后面的程序都是在这个单例已经存在的前提下开始运行。饿汉模式的实现框架相对固定,将静态变量作为单例类的成员变量,类内声明,类外初始化。
饿汉模式的优点:在多线程场景中,由于在进程启动前就已经有一个对象,那么多线程获取单例对象不需要加锁,有效的避免锁资源的竞争,提高性能
饿汉模式的缺点:在编译阶段需要较多的资源,可能导致进程启动较慢
其一般的设计框架的代码如下所示
class Singleton {
private:
Singleton()
:_data(99)
{
/*构造函数的逻辑*/
}
~Singleton()
{
/*析构函数的逻辑*/
}
static Singleton tpins;
int _data;
public:
Singleton& operator=(const Singleton&) = delete;
Singleton(const Singleton&) = delete;
static Singleton& getInstance()
{
return tpins;
}
};
Singleton Singleton::tpins;
懒汉模式
在运行阶段才把单例创建好,后面的程序都是在这个创建好的单例的基础上继续进行,懒汉模式在C++11标准诞生之前和之后有两种实现方式,下面分别用代码实现
第一种:在C++11标准诞生之前实现单例模式的线程池
(注意pthread库需要重新配置)
class LockGuard{
public:
LockGuard(pthread_mutex_t* pmutex)
{
_pmutex = pmutex;
pthread_mutex_lock(_pmutex);
}
~LockGuard()
{
pthread_mutex_unlock(_pmutex);
}
private:
pthread_mutex_t* _pmutex;
};
template <typename T>
class threadPool{
public:
static threadPool<T>* getInstance(){
if(ptpins == nullptr)
{
LockGuard lg(&mutexforsingleModel);
if(ptpins == nullptr)
{
ptpins = new threadPool<T>;
/*执行其他线程池需要的逻辑*/
}
}
return ptpins;
}
private:
static threadPool<T>* ptpins;
static pthread_mutex_t mutexforsingleModel;
int threadNum;
threadPool(const threadPool<T>&) = delete;
threadPool<T>& operator=(const threadPool<T>&) = delete;
threadPool()
:threadNum(8)
{
/*构造函数的逻辑*/
}
};
template <typename T>
threadPool<T>* threadPool<T>::ptpins = nullptr;
template <typename T>
pthread_mutex_t threadPool<T>::mutexforsingleModel = PTHREAD_MUTEX_INITIALIZER;
上面的程序中有几个点值得说明
一、两次检查当前的线程池指针是否为空的作用
外层为空检查:
保证第一次创建线程池时,发现 ptpins == nullptr 成立的线程都会去申请锁资源
只有那个申请锁成功的线程能从 LockGuard lg(&mutexforsingleModel); 这句话之后继续执行
内层为空检查:
只有申请锁成功的线程能够发现 ptpins == nullptr 依然成立,然后去堆上开辟空间
开辟空间之后就会释放锁,其他线程即使在这之后成功申请到锁,也只能直接返回 ptpins
二、锁的设计有多种方式
锁这个对象是线程池一个静态成员变量,
方式一:如下代码,pthread_mutex_lock 和 pthread_mutex_unlock 都需要使用锁的指针作为参数
class LockGuard{
public:
LockGuard(pthread_mutex_t* pmutex)
{
_pmutex = pmutex;
pthread_mutex_lock(_pmutex);
}
~LockGuard()
{
pthread_mutex_unlock(_pmutex);
}
private:
pthread_mutex_t* _pmutex;
};
template <typename T>
class threadPool{
public:
/*获取单例的函数*/
private:
static pthread_mutex_t mutexforsingleModel;
/*其他成员变量*/
};
/*单例构造出来的静态变量*/
template <typename T>
pthread_mutex_t threadPool<T>::mutexforsingleModel = PTHREAD_MUTEX_INITIALIZER;
方式二:unique_lock<mutex> lock(_mtx);
#include <mutex>
template <typename T>
class threadPool {
public:
/*获取单例的函数*/
private:
static mutex _mtx;
/*其他成员变量*/
};
/*获取单例静态变量*/
template <typename T>
mutex threadPool<T>::_mtx;
三、必须把线程池对象在类外初始化成nullptr,
第二种:在C++11标准诞生之后实现单例模式的线程池
template <typename T>
class threadPool{
public:
static threadPool<T>*& getInstance{
static threadPool<T> threadPool;
return &pthreadPool;
}
private:
threadPool(const threadPool<T>&) = delete;
threadPool<T>& operator=(const threadPool<T>&) = delete;
threadPool(int threadNum = defaultNum)
{
/*构造函数的逻辑*/
}
~threadPool()
{
/*析构函数的逻辑*/
}
};
C++11之后实现了只要是在静态的局部变量,第二次再次调用static threadPool<T> threadPool;这句话都不会调用构造函数,也就不会创建新的实例
原文地址:https://blog.csdn.net/weixin_46366676/article/details/142764006
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!