多線程學(xué)習(xí)(五)

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)
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耐齐,隨后出現(xiàn)的幾起案子浪秘,更是在濱河造成了極大的恐慌,老刑警劉巖埠况,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耸携,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡辕翰,警方通過查閱死者的電腦和手機(jī)夺衍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喜命,“玉大人沟沙,你說我怎么就攤上這事”陂牛” “怎么了尝胆?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)护桦。 經(jīng)常有香客問我,道長(zhǎng)煎娇,這世上最難降的妖魔是什么二庵? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮缓呛,結(jié)果婚禮上催享,老公的妹妹穿的比我還像新娘。我一直安慰自己哟绊,他們只是感情好因妙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般攀涵。 火紅的嫁衣襯著肌膚如雪铣耘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天以故,我揣著相機(jī)與錄音蜗细,去河邊找鬼。 笑死怒详,一個(gè)胖子當(dāng)著我的面吹牛炉媒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昆烁,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吊骤,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了静尼?” 一聲冷哼從身側(cè)響起白粉,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茅郎,沒想到半個(gè)月后蜗元,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡系冗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年奕扣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掌敬。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惯豆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奔害,到底是詐尸還是另有隱情楷兽,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布华临,位于F島的核電站芯杀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雅潭。R本人自食惡果不足惜揭厚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扶供。 院中可真熱鬧筛圆,春花似錦、人聲如沸椿浓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至提岔,卻和暖如春仙蛉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唧垦。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工捅儒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人振亮。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓巧还,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親坊秸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子麸祷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 接著上上節(jié) thread ,本節(jié)主要介紹mutex的內(nèi)容褒搔,練習(xí)代碼地址阶牍。<mutex>:該頭文件主要聲明了與互斥量...
    jorion閱讀 12,491評(píng)論 2 4
  • 什么是C++多線程并發(fā)? 線程:線程是操作系統(tǒng)能夠進(jìn)行CPU調(diào)度的最小單位星瘾,它被包含在進(jìn)程之中走孽,一個(gè)進(jìn)程可包含單個(gè)...
    詩(shī)人和酒閱讀 1,665評(píng)論 0 9
  • 最近是恰好寫了一些c++11多線程有關(guān)的東西,就寫一下筆記留著以后自己忘記回來看吧琳状,也不是專門寫給讀者看的磕瓷,我就想...
    編程小世界閱讀 2,501評(píng)論 1 2
  • C++11中std命名空間將Boost庫(kù)中的Thread加入到標(biāo)準(zhǔn)庫(kù) 中,Boost的多線程從準(zhǔn)標(biāo)準(zhǔn)變?yōu)闃?biāo)準(zhǔn)念逞。 創(chuàng)...
    Daisy丶閱讀 3,027評(píng)論 0 3
  • 視頻教程:https://www.bilibili.com/video/av92453755 std::lock_...
    JasonLiThirty閱讀 9,894評(píng)論 0 0