目录
单例设计模式共享数据分析、解决,call_once
设计模式大概谈
- "设计模式":代码的一些写法(这些写法跟常规写法还不怎么一样),程序灵活,维护起来可能方便,但是别人接管、阅读代码都很痛苦。
- 用“设计模式”理念写出来的代码很晦涩的。
- 老外应付特别大的项目的时候,把项目的开发经验、模块划分经验,总结整理成设计模式(现有开发需求,后有理论总结和整理)。
- 不要拿着一个程序(项目)往设计模式上套,一个小小的项目,把它非要弄几个设计模式进去,本末倒置。
- 设计模式肯定有它独特的优点,要活学活用,不要深陷其中,生搬硬套。
单例设计模式
- 单例:整个项目中,有某个或者某些特殊的类,属于该类的对象,我只能创建1个,多了我创建不了。
- 注意delete指针的技巧:类中套类,利用类对象回收时调用析构函数进行指针等资源的释放。
#include <iostream> #include <string> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class singleClass { private: singleClass() {}; //私有化构造函数 static singleClass* m_instance; //静态成员变量 public: static singleClass* GetInstance() { if (m_instance == NULL) { m_instance = new singleClass(); static GuiderPtr gc; } return m_instance; } class GuiderPtr //类中套类,用来释放对象 { public: ~GuiderPtr() { if (singleClass::m_instance) { delete singleClass::m_instance; singleClass::m_instance = NULL; } } }; void func() { cout << "This is a Test" << endl; } }; //类静态变量初始化 singleClass* singleClass::m_instance = NULL; int main() { singleClass* sg_ptr = singleClass::GetInstance(); //创建一个singleClass类,并返回指针 singleClass* sg_ptr1 = singleClass::GetInstance(); //返回的之前创建的指针 sg_ptr->func(); sg_ptr1->func(); singleClass::GetInstance()->func(); return 0; }
单例设计模式共享数据问题分析、解决
- 建议:在创建所有其他线程之前,在主线程中创建出单例对象,加载数据,后续使用。
- 实际可能面临的问题:需要在我们自己创建的线程(而不是主线程)中来创建singleClass这个单例类的对象,这种线程可能不止一个(最少2个)。
- 我们可能会面临GetInstance()这种成员函数要互斥。
- 虽然这两个线程是同一个入口函数,但大家千万要记住,这是两个线程,所以这里会有两个流程(两条通路)同时开始执行mythread这个函数。
考虑加锁方式
#include <iostream> #include <string> #include <thread> #include <mutex> using namespace std; std::mutex res_mutex; class singleClass { private: singleClass() {}; //私有化构造函数 static singleClass* m_instance; //静态成员变量 public: static singleClass* GetInstance() { //提过效率 //a) 如果if (m_instance != nullptr) 条件成立,则肯定表示 m_instance 已经被new过了 //b) 如果if (m_instance == nullptr) 不代表m_instance一定没被new过 if (m_instance == NULL) //双重锁定(双重检查) { std::unique_lock<std::mutex> mutex_getInstance(res_mutex); //自动加锁 if (m_instance == NULL) { m_instance = new singleClass(); static GuiderPtr gc; } } return m_instance; } class GuiderPtr //类中套类,用来释放对象 { public: ~GuiderPtr() { if (singleClass::m_instance) { delete singleClass::m_instance; singleClass::m_instance = NULL; } } }; void func() { cout << "This is a Test" << endl; } }; //类静态变量初始化 singleClass* singleClass::m_instance = NULL; //线程入口函数 void mythread() { cout << "线程函数开始执行了" << endl; singleClass* sgPtr = singleClass::GetInstance(); //这里可能有问题 sgPtr->func(); cout << "线程函数执行完毕了" << endl; return; } int main() { std::thread t_obj1(mythread); std::thread t_obj2(mythread); t_obj2.join(); t_obj1.join(); return 0; }
std::call_once()
- C++11引入的函数,该函数的第二个参数是一个函数名 a()。
- call_once 功能是能够保证函数a()只被调用一次。
- call_once 具备互斥量这种能力,而且效率上,比互斥量消耗的资源更少。
- call_once()需要与一个标记结合使用,这个标记 std::once_flag,其实once_flag是一个结构。
- call_once()就是通过这个标记来决定对应的函数a()是否执行,调用call_once成功后,call_once()就把这个标记设置为一种已调用状态。
- 后续再次调用call_once() ,只要once_flag被设置为了“已调用”状态,那么对应的函数a()就不会再被执行了。
#include <iostream> #include <string> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; std::mutex res_mutex; std::once_flag g_flag; //全局变量,定义的标记 class singleClass { private: singleClass() {}; //私有化构造函数 static singleClass* m_instance; //静态成员变量 static void CreatInstance() //只被调用一次 { std::chrono::milliseconds dura(2000); std::this_thread::sleep_for(dura); cout << "CreatInstance()执行了" << endl; m_instance = new singleClass(); static GuiderPtr gc; } public: static singleClass* GetInstance() { //两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕CreateInstance(); std::call_once(g_flag, CreatInstance); cout << "GetInstance()执行了" << endl; return m_instance; } class GuiderPtr //类中套类,用来释放对象 { public: ~GuiderPtr() { if (singleClass::m_instance) { delete singleClass::m_instance; singleClass::m_instance = NULL; } } }; void func() { cout << "This is a Test" << endl; } }; //类静态变量初始化 singleClass* singleClass::m_instance = NULL; //线程入口函数 void mythread() { cout << "线程函数开始执行了" << endl; singleClass* sgPtr = singleClass::GetInstance(); //这里可能有问题 sgPtr->func(); cout << "线程函数执行完毕了" << endl; return; } int main() { std::thread t_obj1(mythread); std::thread t_obj2(mythread); t_obj2.join(); t_obj1.join(); return 0; }
- 综上,还是建议优先在主线程中先创建单例对象。