6-6 unique_lock詳解

class A
{
public:
	std::unique_lock<std::mutex> rtn_unique_lock()
	{
		std::unique_lock<std::mutex> tmpguard(my_mutex1);
		return tmpguard;//從函數返回一個局部的unique_lock對象是可以的。
		//返回這種局部對象tmpguard會導致系統生成臨時unique_lock對象,並調用unique_lock的移動構造函數
	}
	//把收到的消息(玩家命令)入到一個隊列的線程
	void inMsgRecvQueue()
	{
		for(int i = 0;i<100000;++i)
		{
			cout<<"inMsgRecvQueue()執行,插入一個元素"<<i<<endl;
			//my_mutex2.lock();		//死鎖,這邊先2後1,下邊先1後2
			//my_mutex1.lock();	//實際工程中這兩個鎖頭不一定挨着,們需要保護不同的數據共享塊;
			//std::lock(my_mutex1,my_mutex2);	//相當於每個互斥量都調用了.lock();
			//std::lock_guard<std::mutex> sbguard(my_mutex1,std::adopt_lock);	//這樣能省略自己unlock的步驟
			//std::lock_guard<std::mutex> sbguard(my_mutex2,std::adopt_lock);
			//std::unique_lock<std::mutex> sbguard1(my_mutex1, std::try_to_lock);
			//if(sbguard1.owns_lock()
			//{}else[]
			//std::unique_lock<std::mutex> sbguard1(my_mutex1,std::defer_lock);//沒有加鎖的my_mutex1
			//sbguard1.lock();//用了defer_lock後要自己加鎖解鎖
			//sbguard1.unlock();
			//if(sbguard1.try_lock() == true)	//表示嘗試加鎖成功
			//{
			//}
			//else
			//{
				//沒拿到鎖會幹別的事,不會卡住
			//}
			//std::unique_lock<std::mutex> sbguard1(my_mutex1);
			//std::unique_lock<std::mutex> sbguard2(sbguard1);//複製所有權是非法的
			//std::unique_lock<std::mutex> sbguard2(std::move(sbguard1));//移動語義,現在相當於sbguard2和my_mutex1綁定到一起了,現在sbguard1指向空,sbguard2指向my_mutex1
			//std::mutex *ptx = sbguard1.release();	//現在你有責任自己解鎖這個my_mutex1;
			//ptx->unlock();	//ptx相當於my_mutex1
			std::unique_lock<std::mutex> sbguard1 = rtn_unique_lock();
			msgRecvQueue.push_back(i);//假設這個數字i就是我收到的命令,我直接弄到消息隊列裏邊來;
			//my_mutex.unlock();
		}
		return;
	}
	bool out MsgLULProc(int &command)
	{
		std::lock_guard<std::mutex> sbguard(my_mutex);//sbguard是隨便起的對象名
		//lock_guard構造函數裏執行了mutex::lock(),析構函數裏執行了mutex::unlock();
		//my_mutex1.lock();		//死鎖
		//my_mutex2.lock();
		//std::lock(my_mutex1,my_mutex2);	//相當於每個互斥量都調用了.lock();
		if(!msgRecvQueue.empty())
		{
			//消息不爲空
			int command = msgRecvQueue.front();	//返回第一個元素,但不檢查元素是否存在
			msgRecvQueue.pop_front();	//移除第一個元素
			//my_mutex.unlock();	//一定不能忘記return前的解鎖
			return true;
		}
		//my_mutex.unlock();
		return false;
	}
	//把數據從消息隊列中取出的線程
	void outMsgRecvQueue()
	{
		int command = 0;
		for(int i = 0;i<10000;i++)
		{
			bool result = outMsgLULProc(command);
			if(result == true)
			{
				cout<<"out執行了,取出一個元素"<<command<<endl;
			}
			else
			{
				cout<<"消息隊列爲空"<<i<<endl;
			}
			/*if(!msgRecvQueue.empty())
			{
				//消息不爲空
				int command = msgRecvQueue.front();//返回第一個元素,但不檢查元素是否存在;
				msgRecvQueue.pop_front();	//移除第一個元素,但不返回;
				//這裏就考慮處理數據....
				//....
			}
			else
			{
				//消息隊列爲空
				cout<<"消息隊列爲空"<<i<<endl;
			}*/
		}
		cout<<"end"<<endl;
	}
private:
std::list<int> msgRecvQueue;//容器,專門用於代表玩家給咱們發送過來的命令。
std::mutex my_mutex;	//創建了一個互斥量
};

int main()
{
	A myobja;
	std::thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);//第二個參數是引用才能保證用的是同一個對象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);//
	myOutnMsgObj.join();
	myInMsgObj.join();
}

一:unique_lock取代lock_guard

unique_lock是個類模板,工作中,一般lock_guard(推薦使用);lock_guard取代了mutex的lock()和unlock();
unique_lock比lock_guard靈活很多;效率上差一點,內存佔用多一點。

二:unique_lock的第二個參數

lock_guard可以帶第二個參數:
std::lock_guardstd::mutex sbguard1(my_mutex1,std::adopt_lock); //adopt_lock標記作用;
2.1、std::adopt_lock:表示這個互斥量已經被lock了(你必須要把互斥量提前lock了,否則會報異常)
std::adopt_lock標記的效果就是“假設調用方線程已經擁有了互斥的所有權(已經lock()成功了);通知lock_guard不需要在構造函數中lock這個互斥量了;
unique_lock也可以帶std::adopt_lock標記,含義相同,就是不希望在unique_lock()的構造函數中lock這個mutex。
用這個adopt_lock的前提是,你需要自己先把mutex先lock上;
2.2、std::try_to_lock
我們會嘗試用mutex的lock()去鎖定這個mutex,但如果沒有鎖定成功,我也會立即返回,並不會阻塞在那裏;
用這個try_to_lock的前提是你自己不能先去lock。
2.3、std::defer_lock
用這個defer_lock的前提是你不能自己先lock,否則會報異常。
defer_lock的意思就是並沒有給mutex加鎖:初始化了一個沒有加鎖的mutex。
我們藉着defer_lock的話題,來介紹一些unique_lock的重要成員函數

三:unique_lock的成員函數

3.1、lock(),加鎖
3.2、unlock(),解鎖
3.3、try_lock(),嘗試給互斥量加鎖,如果拿不到鎖,則返回false,如果拿到了鎖,返回true,這個函數不阻塞的;
3.4、release(),返回它所管理的mutex對象指針,並釋放所有權;也就是說,這個unique_lock和mutex不再有關係。
嚴格區分unlock()和release()的區別,不要混淆。
如果原來mutex對象處於加鎖狀態,你有責任接管過來並負責解鎖。(release返回的是原始mutex的指針)
爲什麼有時候需要unlock():因爲你lock鎖住的代碼段越少,執行越快,整個程序運行效率越高。
有人也把鎖頭鎖住的代碼多少稱爲鎖的粒度,粒度一般用粗細來描述;
a、鎖住的代碼少,這個粒度叫細。執行效率高;
b、鎖住的代碼多,粒度叫粗,那執行效率就低;
要學會盡量選擇合適粒度的代碼進行保護,粒度太細,可能漏掉共享數據的保護,粒度太粗,影響效率。
選擇合適的粒度,是高級程序員實力的體現。

四:unique_lock所有權的傳遞,mutex

std::unique_lockstd::mutex sbguard1(my_mutex1):所有權概念
sbguard1擁有my_mutex1的所有權
sbguard1可以把自己對mutex(my_mutex1)的所有權轉移給其他的unique_lock對象;
所以,unique_lock對象這個mutex的所有權是屬於可以轉移的,但是不能複製。

a、std::move
b、return unique_lock

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