unique_lock
前面有一篇講了使用lock_guard替代lock()和unlock(),可以避免忘記unlock()
一.unique_lock取代lock_guard
unique_lock是個(gè)類模板两曼,一般使用lock_guard足夠(取代mutex的lock()和unlock()商佛,推薦使用)
unique_lock比lock_guard靈活很多勾徽,效率上差一點(diǎn)花履,內(nèi)存占用多一點(diǎn)
下面使用unique_lock替代lock_guard測(cè)試:
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue(),插入一個(gè)元素" << i << endl;
//std::lock_guard<std::mutex> myGuard(myMutex); //使用lock_guard避免忘記unlock
std::unique_lock<std::mutex> myUniqueLock(myMutex); //使用unique_lock替代lock_guard
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令服赎,存入消息隊(duì)列
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
//std::lock_guard<std::mutex> myGuard(myMutex); //使用lock_guard避免忘記unlock
std::unique_lock<std::mutex> myUniqueLock(myMutex); //使用unique_lock替代lock_guard
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素媒殉,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行郭宝,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
int main(int argc, char** argv)
{
A obj;
std::thread myOutMsgObj(&A::outMsgRecvQueue, &obj);
//第二個(gè)參數(shù)為引用辞槐,保證子線程操作的是主線程中的obj,不會(huì)復(fù)制(可看上一篇粘室,第二篇博客)
std::thread myInMsgObj(&A::inMsgRecvQueue, &obj); //第二個(gè)參數(shù)為引用
myOutMsgObj.join(); //阻塞主線程并等待子線程執(zhí)行完畢
myInMsgObj.join();
return 0;
}
上述程序使用unique_lock替代lock_guard榄檬,程序正常執(zhí)行。
二.unique_lock的第二個(gè)參數(shù):
lock_guard可以帶第二個(gè)參數(shù):如std::lock_guard<std::mutex> myGuard(myMutex,std::adpot_lock); //adopt_lock起標(biāo)記作用
2.1)std::adopt_lock參數(shù):表示這個(gè)互斥量已經(jīng)被lock()(必須要把互斥量提前l(fā)ock衔统,否則會(huì)報(bào)出異常)
std::adopt_lock標(biāo)記的效果就是 假設(shè)調(diào)用方線程已經(jīng)擁有了互斥的所有權(quán)(已經(jīng)lock()成功了)鹿榜;
通知lock_guard不需要在構(gòu)造函數(shù)中l(wèi)ock()這個(gè)互斥量了
unique_lock也可以帶std::adopt_lock標(biāo)記,含義與lock_guard中的相同锦爵,即不希望在unique_lock()的構(gòu)造函數(shù)中l(wèi)ock這個(gè)mutex
//上面的程序中修改線程函數(shù)部分舱殿,并進(jìn)行測(cè)試:
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue(),插入一個(gè)元素" << i << endl;
//std::lock_guard<std::mutex> myGuard(myMutex); //使用lock_guard避免忘記unlock
//std::unique_lock<std::mutex> myUniqueLock(myMutex); //使用unique_lock替代lock_guard
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令险掀,存入消息隊(duì)列
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
//std::lock_guard<std::mutex> myGuard(myMutex); //使用lock_guard避免忘記unlock
//std::unique_lock<std::mutex> myUniqueLock(myMutex); //使用unique_lock替代lock_guard
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素沪袭,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行樟氢,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
執(zhí)行如上程序冈绊,正常執(zhí)行侠鳄。如果去掉myMutex.lock(),即未提前l(fā)ock(),程序會(huì)報(bào)出異常。
2.2)std::try_to_lock參數(shù)
首先我們做一個(gè)有趣的測(cè)試:向其中一個(gè)線程加入5秒鐘的延時(shí)焚碌,即讓這個(gè)線程停止運(yùn)行5秒鐘畦攘,但我們會(huì)發(fā)現(xiàn)當(dāng)給這個(gè)線程延時(shí)5秒鐘后,另一個(gè)也會(huì)相應(yīng)的延時(shí)5秒鐘
分析:因?yàn)檫@個(gè)線程已經(jīng)拿到鎖了(即lock()成功)十电,進(jìn)入了延時(shí)知押,而還未unlock,另一個(gè)線程自然就無(wú)法獲取到鎖(lock()失敗)鹃骂,也只能停止運(yùn)行台盯,等延時(shí)過后,lock()成功的線程(代碼加入延時(shí)的線程)執(zhí)行完unlock()畏线,另一個(gè)就可以lock()成功了静盅。
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue(),插入一個(gè)元素" << i << endl;
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
//std::chrono::milliseconds duration(20000); //20000毫秒=20秒
//std::this_thread::sleep_for(duration); //停止運(yùn)行20秒
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令寝殴,存入消息隊(duì)列
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
std::chrono::milliseconds duration(5000); //5000毫秒=5秒
std::this_thread::sleep_for(duration); //停止運(yùn)行5秒
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素蒿叠,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行蚣常,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
下面是程序的執(zhí)行結(jié)果市咽,可以看出每過5秒鐘,兩個(gè)線程繼續(xù)執(zhí)行抵蚊。
std::try_to_lock的作用:嘗試lock(),但如果沒有鎖定成功施绎,也會(huì)立即返回(可以查看是否lock()成功),并且不會(huì)阻塞在那里
用這個(gè)try_to_lock的前提是不能先去lock()
//下面我們使用try_to_lock測(cè)試(避免上面的情況出現(xiàn)贞绳,即一個(gè)程序延時(shí)谷醉,另一個(gè)程序也延時(shí),這里冈闭,我們會(huì)讓另一個(gè)線程去執(zhí)行其他任務(wù)俱尼,不會(huì)一直卡死):
修改線程函數(shù)如下:
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue(),插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::try_to_lock);
//使用try_to_lock()參數(shù)拒秘,前面如果已經(jīng)lock(),程序會(huì)崩潰
if (myUniqueLock.owns_lock()) //判斷是否拿到鎖
{
//拿到了鎖:lock成功
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令号显,存入消息隊(duì)列
}
else
{
//沒拿到鎖
cout << "inMsgRecvQueue()執(zhí)行,但沒有拿到鎖..." << i << endl;
}
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
std::chrono::milliseconds duration(5000); //5000毫秒=5秒
std::this_thread::sleep_for(duration); //停止運(yùn)行5秒
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素躺酒,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行押蚤,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
執(zhí)行上面的程序羹应,如下圖所示揽碘,發(fā)現(xiàn)另一個(gè)線程(代碼未加入延時(shí)的線程)不會(huì)卡死,而是一直在執(zhí)行其他任務(wù),這就是使用try_to_lock的好處雳刺。
注意:使用try_to_lock不能在前面lock()
2.3)std::defer_lock參數(shù)
使用std::defer_lock的前提是也不能先執(zhí)行l(wèi)ock(),否則會(huì)報(bào)出異常
defer_lock的意思就是初始化了一個(gè)沒有加鎖的mutex
相當(dāng)于將unique_lock和mutex綁定在一起劫灶,用unique_lock管理mutex
借著defer_lock的話題,介紹一些unique_lock的重要成員函數(shù),請(qǐng)看下文
三.unique_lock的成員函數(shù)
3.1)lock(),加鎖
修改線程函數(shù)如下:
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()掖桦,插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::defer_lock);
//創(chuàng)建沒有加鎖的myMutex本昏,也不能提前l(fā)ock()
myUniqueLock.lock(); //注意不是mutex的成員函數(shù)lock(),且之后不用自己手動(dòng)unlock()
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令,存入消息隊(duì)列
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素枪汪,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行涌穆,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
使用修改后的線程函數(shù)雀久,程序依然可以正常執(zhí)行宿稀,注意此處的介紹的lock()函數(shù)并不是mutex類的成員函數(shù),且之后不用自己手動(dòng)unlock()(當(dāng)然也可以自己手動(dòng)unlock())
3.2)unlock()
疑惑:明明可以自動(dòng)解鎖赖捌,為什么還要有unlock()
解答:使用unlock()可以在程序中臨時(shí)處理一些非共享代碼祝沸,處理完之后再lock()。越庇,lock()鎖住的代碼段越少罩锐,程序運(yùn)行效率越高
當(dāng)然,雖然上面的程序測(cè)試了使用lock()時(shí)不用自己手動(dòng)unlock(),但也可以自己手動(dòng)unlock()(畫蛇添足)
lock()鎖住的代碼段的多少稱為鎖的 粒度卤唉,粒度一般用粗細(xì)來描述
a)lock()鎖住的代碼少唯欣,這個(gè)粒度叫細(xì),執(zhí)行效率高搬味。
b)lock()鎖住的代碼多,這個(gè)粒度叫粗蟀拷,執(zhí)行效率低碰纬。
盡量選擇合適粒度的代碼進(jìn)行保護(hù),粒度太細(xì)问芬,可能漏掉共享數(shù)據(jù)的保護(hù)悦析,粒度太粗,影響效率此衅。
修改后的線程函數(shù)如下:
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()强戴,插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::defer_lock);
//創(chuàng)建沒有加鎖的myMutex,也不能提前l(fā)ock()
myUniqueLock.lock(); //注意不是mutex的成員函數(shù)lock(),且之后不用自己手動(dòng)unlock()
//下方處理共享代碼
//...
//因?yàn)橛幸恍┓枪蚕泶a要處理
myUniqueLock.unlock();
//下方處理非共享代碼
//...
//處理完之后再進(jìn)行l(wèi)ock()
myUniqueLock.lock();
//下方可處理共享代碼
//...
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令挡鞍,存入消息隊(duì)列
myUniqueLock.unlock(); //畫蛇添足骑歹,但也可以
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行墨微,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行道媚,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
使用修改后的線程函數(shù)運(yùn)行程序,演示了unlock()函數(shù)怎么用來臨時(shí)處理非共享代碼。
3.3)try_lock()
類似于mutex類的try_to_lock()函數(shù)最域,嘗試給互斥量加鎖谴分,如果拿不到鎖(lock()失敗),則返回false,否則返回true镀脂,這個(gè)函數(shù)是不阻塞的
修改后的線程函數(shù)如下:
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()牺蹄,插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::defer_lock);
//創(chuàng)建沒有加鎖的myMutex,也不能提前l(fā)ock()
if (myUniqueLock.try_lock()) //返回true表示達(dá)到鎖(lock()成功)
{
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令薄翅,存入消息隊(duì)列
}
else
{
//返回false(lock()失敗)
cout << "inMsgRecvQueue()執(zhí)行沙兰,但沒有拿到鎖" << i << endl;
}
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
std::chrono::milliseconds duration(5000); //5000毫秒=5秒
std::this_thread::sleep_for(duration); //停止運(yùn)行5秒
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行匿刮,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行僧凰,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
上面的程序在其中一個(gè)線程延時(shí)了5秒,且還沒有unlock(),另一個(gè)程序在這5秒之內(nèi)無(wú)法獲得鎖(無(wú)法lock()成功)熟丸,使用try_lock()不斷嘗試加鎖训措,5秒過后可獲得鎖(lock()成功)。
3.4)release()
返回它所管理的mutex對(duì)象指針光羞,并釋放所有權(quán)绩鸣,即unique_lock和mutex不再有關(guān)系
不要混淆unlock()和release()
如果原來mutex對(duì)象處于加鎖狀態(tài),需要手動(dòng)解鎖纱兑。
修改后的線程函數(shù)如下:
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()呀闻,插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex); //相當(dāng)于mutex已經(jīng)lock()
std::mutex* ptrMutex = myUniqueLock.release();
//將unique_lock和mutex分離,現(xiàn)在需手動(dòng)解鎖mutex(unlock())潜慎,返回指向原來mutex的指針
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令捡多,存入消息隊(duì)列
ptrMutex->unlock(); //自己需手動(dòng)unlock()
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
std::chrono::milliseconds duration(5000); //5000毫秒=5秒
std::this_thread::sleep_for(duration); //停止運(yùn)行5秒
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行铐炫,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行垒手,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
上述程序使用releas()分離了unique_lock和mutex后,后面需手動(dòng)解鎖(unlock())
四.unique_lock所有權(quán)的傳遞(一個(gè)unique_lock對(duì)應(yīng)一個(gè)mutex)
std::unique_lock<std::mutex> myUniqueLock(myMutex);
所有權(quán)概念:myUniqueLock擁有myMutex的所有權(quán)
myUniqueLock可以把自己對(duì)myMutex的所有權(quán)轉(zhuǎn)移給其他的unique_lock對(duì)象
所有unique_lock對(duì)象的mutex的所有權(quán)可以轉(zhuǎn)移倒信,但是不能復(fù)制科贬。類似于智能指針unique_ptr
下面修改的線程函數(shù)演示了所有權(quán)轉(zhuǎn)移的方法,以及驗(yàn)證了所有權(quán)不能復(fù)制鳖悠。
a)使用std::move轉(zhuǎn)移
class A
{
public:
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()榜掌,插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock1(myMutex); //相當(dāng)于mutex已經(jīng)lock()
//std::unique_lock<std::mutex> myUniqueLock2(myMutex);
//再使用myMutex創(chuàng)建一個(gè)unique_lock對(duì)象,會(huì)發(fā)現(xiàn)出錯(cuò)乘综,相當(dāng)于lock()兩次憎账,程序崩潰
//std::unique_lock<std::mutex> myUniqueLock2(myUniqueLock);
//復(fù)制myMutex的所有權(quán),會(huì)發(fā)現(xiàn)直接報(bào)錯(cuò)瘾带,此操作非法
std::unique_lock<std::mutex> myUniqueLock2(std::move(myUniqueLock1));
//將myMutex的所有權(quán)傳遞給myUniqueLock2鼠哥,相當(dāng)于myUniqueLock2與myMutex現(xiàn)在綁定在一起
//而myUniqueLock1指向空熟菲,相當(dāng)于release()
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令,存入消息隊(duì)列
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素朴恳,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行抄罕,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};
//b)return std::unique_lock<std::mutex>將臨時(shí)對(duì)象擁有的所有權(quán)進(jìn)行轉(zhuǎn)移
下面修改的線程函數(shù)采取返回臨時(shí)對(duì)象的方法轉(zhuǎn)移所有權(quán)于颖。
class A
{
public:
std::unique_lock<std::mutex> retUniqueLock()
{
std::unique_lock<std::mutex> tmpUniqueLock(myMutex);
return tmpUniqueLock;
//返回一個(gè)局部的unique_lock對(duì)象是可以的呆贿,系統(tǒng)生成臨時(shí)unique_lock對(duì)象,并調(diào)用unique_lock的移動(dòng)構(gòu)造函數(shù)
}
//線程:把收到的消息(玩家命令)放入一個(gè)隊(duì)列
void inMsgRecvQueue()
{
for (int i = 0; i < 100000; i++)
{
cout << "inMsgRecvQueue()森渐,插入一個(gè)元素" << i << endl;
std::unique_lock<std::mutex> myUniqueLock = retUniqueLock();//所有權(quán)轉(zhuǎn)移
msgRecvQueue.push_back(i); //假設(shè)i就是收到的命令做入,存入消息隊(duì)列
}
return;
}
//判斷消息是否為空,不為空取出命令
bool outMsgProc(int& command)
{
myMutex.lock();
std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
//帶std::adopt_lock參數(shù)必須提前l(fā)ock()
if (!msgRecvQueue.empty())
{
//消息不為空
command = msgRecvQueue.front(); //返回第一個(gè)元素
msgRecvQueue.pop_front(); //移除第一個(gè)元素,但不返回
//處理取出的數(shù)據(jù)
return true;
}
return false;
}
//線程:把收到的命令取出
void outMsgRecvQueue()
{
int command;
for (int i = 0; i < 100000; i++)
{
bool result = outMsgProc(command);
if (result)
{
cout << "outMsgRecvQueue()執(zhí)行同衣,取出一個(gè)命令" << command << endl;
//處理命令
}
else
{
//消息隊(duì)列為空
cout << "outMsgRecvQueue()執(zhí)行竟块,但目前消息隊(duì)列為空" << i << endl;
}
}
cout << "end" << endl;
}
private:
list<int> msgRecvQueue; //存放玩家發(fā)送的命令隊(duì)列(共享數(shù)據(jù))
mutex myMutex; //創(chuàng)建了一個(gè)互斥量(鎖1)
};