在实现数据库连接池的过程中使用到了单例模式,本文进行进一步拓展和扩充。
1.单例模式是什么?优点是什么?
- 单例模式是指在整个系统生命周期内,保证一个类只能产生一个实例,确保该类的唯一性。
- 优点:(1)节省资源。一个类只有一个实例,不存在多份实例,节省资源。(2)方便控制。在一些操作公共资源的场景时,避免了多个对象引起的复杂操作。
2. 什么是线程安全,怎么保证线程安全?
- 在拥有动向数据的多条线程并行执行的程序中,线程安全的代码可以通过同步机制保证各个线程可以正常且正确的执行,不会出现内存泄露和数据污染的情况。
- 保证线程安全可以通过给共享资源加锁的方式进行,保证每个资源变量每时每刻最多只能有一个线程被占用;或者让线程也拥有资源,不需要去共享进程的资源。
本文介绍单例模式的4种C++实现
一、懒汉式:
1. 懒汉式单例模式
存在问题:
1,线程不安全-->加锁可以解决此问题
2,内存泄露-->使用智能指针解决此问题
class SingletonPatten_V1 {
private:
SingletonPatten_V1() {
cout << "构造" << endl;
}
SingletonPatten_V1(SingletonPatten_V1&) = delete;//禁止拷贝构造函数
SingletonPatten_V1& operator=(const SingletonPatten_V1&) = delete;//禁止赋值运算符重载
static SingletonPatten_V1* m_pInstance;//赋值静态指针成员变量,负责调用唯一实例
public:
~SingletonPatten_V1() {
cout << "析构" << endl;
}
static SingletonPatten_V1* Instance() {
if (!m_pInstance) {
m_pInstance = new SingletonPatten_V1();
}
return m_pInstance;
}
void use() const {
cout << "inuse" << endl;
}
};
//类外初始化静态变量
SingletonPatten_V1* SingletonPatten_V1::m_pInstance = nullptr;
2. 加锁的懒汉式单例(线程安全)
使用互斥锁保证线程安全
class SingletonPatten_V2 {
/*
改进:
1,加锁
2,使用智能指针
*/
public:
~SingletonPatten_V2() {
cout << "析构" << endl;
}
SingletonPatten_V2(SingletonPatten_V2&) = delete;//禁止拷贝构造函数
SingletonPatten_V2& operator=(const SingletonPatten_V2&) = delete;//禁止赋值运算符重载
//使用静态智能指针实例化
static shared_ptr<SingletonPatten_V2> Instance() {
/*1.第一个if语句用于检查单例实例是否已经被创建。如果未被创建(即m_pInstance为空),
则进入if语句块内部执行。这个检查是在不加锁的情况下进行的,以提高性能。
2.如果第一个if条件为真,进入if语句块内部后,使用互斥锁来确保线程安全。这样,在多
线程环境下,只有第一个到达的线程会进入这个if语句块内部,其他线程会被阻塞在互斥锁
上,直到锁被释放。
3.在第二个if语句中再次检查m_pInstance是否为空。这是为了避免在多线程情况下出现竞争
条件。即使在第一个if语句块内使用了互斥锁,但是在线程被阻塞时,其他线程可能已经
在锁被释放后进入了临界区。因此,再次检查m_pInstance为空是必要的。
4.如果第二个if条件为真,表明单例实例仍未被创建,则在互斥锁保护下创建单例实例,
并将其赋值给m_pInstance。
5.最后,返回单例实例。*/
if (m_pInstance==nullptr) {
lock_guard<mutex> lk(mutex);
if (m_pInstance == nullptr) {
m_pInstance = shared_ptr<SingletonPatten_V2>(new SingletonPatten_V2());
}
}
return m_pInstance;
}
void use() const { //在函数内部不会对对象的状态进行修改
cout << "inuse" << endl;
}
private:
SingletonPatten_V2() {
cout << "构造" << endl;
}
//定义智能指针
static shared_ptr<SingletonPatten_V2> m_pInstance;
//静态锁
static mutex m_mutex;
};
//在C++中,静态成员变量必须在类的外部进行初始化。这是因为静态成员变量属于类的整体,而不是属于类的具体对象。
shared_ptr<SingletonPatten_V2> SingletonPatten_V2::m_pInstance = nullptr;
mutex SingletonPatten_V2::m_mutex;
3. 静态局部变量的懒汉单例(C++11线程安全)
在 C++11 中,静态局部变量这种方式天然是线程安全的,不存在线程安全的问题
//改进实例3
class SingletonPatten_V3 {
//使用局部变量
public:
~SingletonPatten_V3() {
cout << "析构" << endl;
}
SingletonPatten_V3(const SingletonPatten_V3&) = delete;
SingletonPatten_V3& operator=(const SingletonPatten_V3&) = delete;
//实例化
//当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。
static SingletonPatten_V3& Instance() {
static SingletonPatten_V3 m_pIntance;//C++静态变量的生存期 是从声明到程序结束,这也是一种懒汉式
return m_pIntance;
}
private:
SingletonPatten_V3() {
cout << "构造" << endl;
}
};
二、饿汉式
4.饿汉式单例模式实现
class SingletonPatten_V4 {
public:
//获取单实例
static SingletonPatten_V4* getInstance() {
return m_pInstance;
}
//释放单实例
static void deleteInstance() {
delete m_pInstance;
m_pInstance = nullptr;
}
//打印实例
void print() {
cout << this << endl;
}
private:
//私有构造和析构
SingletonPatten_V4() {
cout << "V4构造函数" << endl;
}
~SingletonPatten_V4() {
cout << "V4析构函数" << endl;
}
//私有拷贝构造和赋值
SingletonPatten_V4(const SingletonPatten_V4& signal) = delete;
const SingletonPatten_V4& operator=(const SingletonPatten_V4& signal) = delete;
private:
//唯一的单实例对象指针
static SingletonPatten_V4* m_pInstance;
};
//初始化静态成员变量,在程序一开始时就进行创建
SingletonPatten_V4* SingletonPatten_V4::m_pInstance = new SingletonPatten_V4();
int main() {
//获取单例进行调用
SingletonPatten_V4* p4 = SingletonPatten_V4::getInstance();
p4->print();
system("pause");
return 0;
}