七、单例设计模式
设计模式:代码的一些写法:程序灵活,维护起来可能方便,但是别人接管比较麻烦。
设计模式肯定有它独特的有点,要活学活用,不要深陷其中,不要为了用设计模式而用设计模式。
单例设计模式
单例类:整个项目只能创建一个对象。
单线程单例步骤:构造函数初始化 =》静态私有成员变量 =》公有静态函数,返回类对象指针。
那怎么释放这个类对象=》类中再使用一个类。
函数判断:如果单例类没有初始化,就初始化呗。=》一个null判断
初始化要加锁:
#include <thread> #include <iostream> #include <list> #include <mutex> using namespace std; std::mutex resource_mutex; class MyCAS { private: MyCAS() {} //私有化构造函数 private: static MyCAS *m_instance; public: static MyCAS *GetInstance() { std::unique_lock<std::mutex> mymutex(resource_mutex); //此方法可行,但是效率太低,这个 锁 实际上只用于第一次创建MyCAS时 //但是,之后每次执行时都必须调用它 if (m_instance == nullptr) { m_instance = new MyCAS(); static CCarhuishou cl; } return m_instance; } class CCarhuishou { //用来释放对象 public: ~CCarhuishou() { if (MyCAS::m_instance) { delete MyCAS::m_instance; MyCAS::m_instance == nullptr; } } }; void func() { cout << "测试" << endl; } }; MyCAS *MyCAS::m_instance = nullptr; void mythread() { cout << "我的线程开始了" << endl; MyCAS *pa = MyCAS::GetInstance(); cout << "我的线程结束了" << endl; } int main() { //这里是两个线程,所以是两个通路同时执行GetInstance() //这时存在 m_instance = new MyCAS(); 被执行多次的可能 std::thread myobj1(mythread); std::thread myobj2(mythread); }
是这样有个问题:
很多线程都调用这个静态函数,那就每次都需要创建锁,锁开销很大,因此在加锁时需要再判断一次单例类是否为空,为空就加锁创建对象了。
=》那里面的锁是不是可以不要了
可能存在这样的情况:就是线程A第一个判断过了,然后申请锁失败了,就等着;等的过程线程B把对象创建了,此时线程A获得锁,不加判断的话会重复创建。
#include <thread> #include <iostream> #include <list> #include <mutex> using namespace std; std::mutex resource_mutex; class MyCAS { private: MyCAS() {} //私有化构造函数 private: static MyCAS *m_instance; public: static MyCAS *GetInstance() { //双重锁定(双重检查),提高效率 if (m_instance == nullptr) { //第一次判断是用于跳过 初始化之后的判断 std::unique_lock<std::mutex> mymutex(resource_mutex); if (m_instance == nullptr) { //第二次判断之前加锁 用于 防止初始化时重复初始化 m_instance = new MyCAS(); static CCarhuishou cl; } } return m_instance; } class CCarhuishou { //用来释放对象 public: ~CCarhuishou() { if (MyCAS::m_instance) { delete MyCAS::m_instance; MyCAS::m_instance == nullptr; } } }; void func() { cout << "测试" << endl; } }; MyCAS *MyCAS::m_instance = nullptr; void mythread() { cout << "我的线程开始了" << endl; MyCAS *pa = MyCAS::GetInstance(); cout << "我的线程结束了" << endl; } int main() { //这里是两个线程,所以是两个通路同时执行GetInstance() //这时存在 m_instance = new MyCAS(); 被执行多次的可能 std::thread myobj1(mythread); std::thread myobj2(mythread); }
std::call_once()函数模板
它的功能是能够保证函数a只被调用一次。它具备互斥量这种能力,而且效率上,比互斥量消耗的资源更少;
call_once()需要与一个标记结合使用,这个标记 std::once_flag;其实 once_flag 是一个结构
call_once()就是通过这个表姐来决定对应的函数a()是否执行,调用call_once()成功后,call_once()就把这个标记设置为一种已调用状态。
#include <thread> #include <iostream> #include <list> #include <mutex> using namespace std; std::mutex resource_mutex; std::once_flag g_flag; class MyCAS { private: MyCAS() {} //私有化构造函数 static void CreateInstance() { m_instance = new MyCAS(); static CCarhuishou cl; } private: static MyCAS *m_instance; public: static MyCAS *GetInstance() { std::call_once(g_flag, CreateInstance); //这里可以把 g_flag 看成一把锁 return m_instance; } class CCarhuishou { //用来释放对象 public: ~CCarhuishou() { if (MyCAS::m_instance) { delete MyCAS::m_instance; MyCAS::m_instance == nullptr; } } }; void func() { cout << "测试" << endl; } }; MyCAS *MyCAS::m_instance = nullptr; void mythread() { cout << "我的线程开始了" << endl; MyCAS *pa = MyCAS::GetInstance(); cout << "我的线程结束了" << endl; } int main() { //这里是两个线程,所以是两个通路同时执行GetInstance() //这时存在 m_instance = new MyCAS(); 被执行多次的可能 std::thread myobj1(mythread); std::thread myobj2(mythread); }
心之所愿,永不相忘