一:設計模式大概談
“設計模式”:代碼的一些寫法(這些寫法跟常規寫法不怎麼一樣);程序靈活,維護起來可能方便,但是別人接管、閱讀代碼都會很痛苦;
用“設計模式”理念寫出來的代碼很晦澀;《head first》
老外應付特別大的項目的時候,把項目的開發經驗、模塊劃分經驗,總結整理成設計模式(現有開發需求,後有理論總結和整理);
設計模式拿到中國來,不太一樣,拿着一個程序(項目)往設計模式上套;一個小小的項目,它非要弄幾個設計模式進去,本末倒置
設計模式肯定有它獨特的優點,要活學活用,不要深陷堪中,生搬硬套;
二:單例設計模式
單例設計模式,使用的頻率比較高;
單例:整個項目中,有某個或者某些特殊的類,屬於該類的對象,我只能創建1個多了我創建不了;
單例類;
std::mutex_resource_mutex;
std::once_flag g_flag; //這是個系統定義的標記
class MyCAS //這是一個單例類
{
static void CreateInstance() //只被調用一次的函數
{
//std::chrono::milliseconds dura(20000);//鎖200毫秒用於測試
//std::this_thread::sleep_for(dura);
m_instance = new MyCAS();
static CGarhuishou c1;
}
private:
MyCAS(){} //私有化了構造函數
private:
static MyCAS *m_instance;//靜態成員變量
public:
static MyCAS *GetInstance()
{
//提高效率。
//a、如果 if(m_instance != NULL)條件成立,則肯定表示m_instance已經被new過了;
//b、如果 if(m_instance==NULL),不代表m_instance一定沒被new過;
/*if(m_instance == NULL)//雙重鎖定(雙重檢查)
{
std::unique_lock<std::mutex> mymutex(resource_mutex);//自動加鎖
if(m_instance == NULL)
{
m_instance = new MyCAS();
}
}*/
std::call_once(g_flag, CreateInstance);//假設兩個線程同時執行到這裏,其中一個線程要等另外一個線程執行完畢createinstance();
return m_instance;
}
class CGarhuishou //類中套類,用來釋放對象
{
public:
~CGarhuishou() //類的析構函數中
{
if(MyCAS::m_instance)
{
delete MyCAS::m_instance;
MyCAS::m_instance = NULL;
}
}
};
void func()
{
cout<<"測試“<<endl;
}
};
//類靜態變量初始化
MyCAS *MyCAS::m_instance = NULL;
void main()
{
MyCAS *p_a = MyCAS::GetInstance();//創建一個對象指針,返回該類(MyCAS)對象的指針;
MyCAS *p_b = MyCAS::GetInstance();
//MyCAS A1; //私有化構造函數後無法創建只能通過GetInstance();
p_a->func();
MyCAS::GetInstance()->func();
}
三:單例設計模式共享數據問題分析、解決
面臨的問題:需要在我們自己創建的線程(而不是主線程)中來創建MyCAS這個單例類的對象,這種線程可能不止(最少2個)。
我們可能會面臨GetInstance()這種成員函數要互斥;
雖然這兩個線程是同一個入口函數,但大家千萬要記住,這是兩個線程,所以這裏會有兩個流程(兩條通路)同時開始執行mythread
std::thread mytobj1(mythread);
std::thread mytobj2(mythread);
mytobj1.join();
mytobj2.join();
四: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()就不會再被執行了;
總結:
一般在主線程創建單例對象,在子線程中進行使用