(C++11/14/17學習筆記):單例設計模式共享資料分析,call_once()函式
阿新 • • 發佈:2020-12-10
技術標籤:C++11/14/17
目錄
單例設計模式共享資料分析、解決,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; }
- 綜上,還是建議優先在主執行緒中先建立單例物件。