6-7 單例設計模式共享數據分析、解決,call_once

一:設計模式大概談

“設計模式”:代碼的一些寫法(這些寫法跟常規寫法不怎麼一樣);程序靈活,維護起來可能方便,但是別人接管、閱讀代碼都會很痛苦;
用“設計模式”理念寫出來的代碼很晦澀;《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()就不會再被執行了;

總結:

一般在主線程創建單例對象,在子線程中進行使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章