#include<iostream> #include<string> #include<vector> #include<mutex> #include<thread> using namespace std; std::mutex resourece_mutex; std::once_flag g_flag;//这是一个系统定义的标记 class mycas //这是一个单例类 { static void CreateInstance() {//只被调用一次的函数 m_instance = new mycas(); static CGarhuishou cj; } private: mycas() {}//私有化了构造函数 private: static mycas* m_instance; public: static mycas* Getinstance() { 提高效率 a)if(m_instance != nullptr)如果这个成立,则表示m_instance已经被new过了; b)如果if(m_instance == nullptr)不代表m_instance一定没被new过 //if (m_instance == nullptr) {//双重锁定(双重检查) // std::unique_lock<std::mutex> mymutex(resourece_mutex);//自动加锁 // if (m_instance == nullptr) { // m_instance = new mycas(); // static CGarhuishou cj; // } //} //因为要写到call_once()所以将这一块代码注释掉
std::call_once(g_flag, CreateInstance);//两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕CreateInstance //这里可以吧这个g_flag看成一个锁。 return m_instance;
}
class CGarhuishou {//类中套类,用来释放对象 public: ~CGarhuishou() { if (mycas::m_instance) { delete mycas::m_instance; mycas::m_instance = nullptr; } } };
void function() { cout << "测试" << endl; } }; //类静态变量初始化 mycas* mycas::m_instance = nullptr;
//线程入口函数 void mythread() { cout << "线程开始执行了" << endl; mycas* pa = mycas::Getinstance();//这里可能有问题 cout << "线程执行完毕了" << endl; } int main() { /* (1)设计模式大概谈 * “设计模式”:代码的一些写法,(这些写法跟常规写法不太一样):程序灵活,维护起来可能方便,但是别人接管,阅读代码很痛苦 * 用设计模式写出来的代码是很晦涩的;《head first》 * 老外为了应付很大的项目时候,吧项目的开发经验、模块划分经验,总结整理成设计模式。(先有开发需求,后有理论总结和整理); 设计模式拿到中国来,不太一样,拿着一个程序(项目)往设计模式上套;一个小小的项目,它非要弄几个设计模式进去,本末倒置 设计模式肯定有它独特的优点,活学活用,不要生搬硬套; * (2)单例设计模式 * 单例设计模式,使用的频率高, * 单例:整个项目中,有某个或者某些特殊的类,属于该类的对象,我只能创建一个,多了我创建不了, * 单例类; * 注意:不能再析构函数中释放自身对象 * 因为析构一个对象,是需要调用这个对象的析构函数,如果在析构函数里面delete自身对象,那么delete中又要调用析构函数,陷入析构循环。 * (3)单例设计模式共享数据问题分析,解决 * 最好在主线程创建这个单例类,在其他线程创建之前就创建这个单例类,并且完成初始化,把该装载的数据装载,数据为只读 * 实际中面临的问题: 需要在我们自己创建的线程而不是主线程中来创建这个mycas这个单例类的对象,这种线程还不止一个 * 我们可能要面临Getinstance()这种成员函数要互斥; * 问题1:可能第一个线程在执行m_instance = new mycas();的时候,第二个线程判断m_instance == nullptr,也会去new这个对象,这样就会出现问题 * 解决办法:加锁 * 问题2:在if上面直接加锁,只是为了第一次new这个对象而加个锁,这样在每次使用这个函数的时候都会进行加锁,效率大大降低 * 解决办法:双重检查。提高效率。只有在线程才开始的时候,多个线程进行判断,加锁,一旦一个加锁成功,创建对象成功,其他线程就直接返回就OK了, * 下次再进行创建对象时候,也不用加锁,直接返回该对象就好了 * (3)std::call_once() C++引入的函数,该函数的第二个参数是一个函数名a(); * 功能:能够保证函数a只被调用一次。即便有许多线程,每个线程都调用了这个函数,只要用了这个函数,编译器就会保证这个函数a只被调用一次。 * 具备互斥量的能力,效率上,比互斥量消耗的资源更少; * call_once()需要和一个标记结合使用,这个标记是std::once_flag;其实once_flag是一个结构 * call_once()通过这个标记来决定对应的函数()是否执行,调用call_once()成功后,call_once()就把这个标记设置为已调用状态 * 后续再次调用call_once()时候,只要这个标记为已调用状态,那么对应的函数a就不会再被执行。 * */ /*mycas m; mycas m2;*///构造函数私有化了,不能通过这个方式生成类对象。 mycas* p_a = mycas::Getinstance();//创建一个对象,返回该类(mycas)对象的指针; p_a->function();
//如下两个线程是同一个入口函数,但是要记住,这是两个线程,所以这里会有两个流程,或者两条通路同时执行mythread这个函数。同时执行getrinstance就可能出问题 std::thread mytobj1(mythread); std::thread mytobj2(mythread); const int aaa=10; mytobj1.join(); mytobj2.join(); }