美文网首页
多线程学习(五)

多线程学习(五)

作者: lxr_ | 来源:发表于2021-11-14 20:45 被阅读0次

unique_lock

前面有一篇讲了使用lock_guard替代lock()和unlock(),可以避免忘记unlock()

一.unique_lock取代lock_guard

unique_lock是个类模板,一般使用lock_guard足够(取代mutex的lock()和unlock(),推荐使用)
unique_lock比lock_guard灵活很多,效率上差一点内存占用多一点
下面使用unique_lock替代lock_guard测试

#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
#include <list>

using namespace std;

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << 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);    //假设i就是收到的命令,存入消息队列
        }
        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();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }

    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};
int main(int argc, char** argv)
{
    A obj;

    std::thread myOutMsgObj(&A::outMsgRecvQueue, &obj);     
    //第二个参数为引用,保证子线程操作的是主线程中的obj,不会复制(可看上一篇,第二篇博客)

    std::thread myInMsgObj(&A::inMsgRecvQueue, &obj);       //第二个参数未引用

    myOutMsgObj.join();                                     //阻塞主线程并等待子线程执行完毕
    myInMsgObj.join();

    return 0;
}

上述程序使用unique_lock替代lock_guard,程序正常执行。

二.unique_lock的第二个参数:

lock_guard可以带第二个参数:如std::lock_guard<std::mutex> myGuard(myMutex,std::adpot_lock); //adopt_lock起标记作用
2.1)std::adopt_lock参数:表示这个互斥量已经被lock()(必须要把互斥量提前lock,否则会报出异常)
std::adopt_lock标记的效果就是 假设调用方线程已经拥有了互斥的所有权(已经lock()成功了);
通知lock_guard不需要构造函数中lock()这个互斥量
unique_lock也可以带std::adopt_lock标记含义与lock_guard中的相同,即不希望在unique_lock()的构造函数中lock这个mutex
//上面的程序中修改线程函数部分,并进行测试:

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << 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参数必须提前lock()

            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
        }
        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参数必须提前lock()

        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }
private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

执行如上程序,正常执行。如果去掉myMutex.lock(),即未提前lock(),程序会报出异常。
2.2)std::try_to_lock参数
首先我们做一个有趣的测试:向其中一个线程加入5秒钟的延时,即让这个线程停止运行5秒钟,但我们会发现当给这个线程延时5秒钟后,另一个也会相应的延时5秒钟
分析:因为这个线程已经拿到锁了(即lock()成功),进入了延时,而还未unlock,另一个线程自然就无法获取到锁(lock()失败),也只能停止运行,等延时过后,lock()成功的线程(代码加入延时的线程)执行完unlock(),另一个就可以lock()成功了。

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;

            myMutex.lock();
            std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
            //带std::adopt_lock参数必须提前lock()

            //std::chrono::milliseconds duration(20000);      //20000毫秒=20秒
            //std::this_thread::sleep_for(duration);          //停止运行20秒


            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
        }
        return;
    }

    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()

        std::chrono::milliseconds duration(5000);      //5000毫秒=5秒
        std::this_thread::sleep_for(duration);          //停止运行5秒

        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

下面是程序的执行结果,可以看出每过5秒钟,两个线程继续执行。




std::try_to_lock的作用尝试lock(),但如果没有锁定成功,也会立即返回(可以查看是否lock()成功),并且不会阻塞在那里
用这个try_to_lock的前提是不能先去lock()
//下面我们使用try_to_lock测试(避免上面的情况出现,即一个程序延时,另一个程序也延时,这里,我们会让另一个线程去执行其他任务,不会一直卡死):
修改线程函数如下:

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;
    
    
            std::unique_lock<std::mutex> myUniqueLock(myMutex, std::try_to_lock); 
            //使用try_to_lock()参数,前面如果已经lock(),程序会崩溃
            if (myUniqueLock.owns_lock())     //判断是否拿到锁
            {
                //拿到了锁:lock成功
                msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
            }
            else
            {
                //没拿到锁
                cout << "inMsgRecvQueue()执行,但没有拿到锁..." << i << endl;
            }
        }
        return;
    }
    
    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()
    
        std::chrono::milliseconds duration(5000);      //5000毫秒=5秒
        std::this_thread::sleep_for(duration);          //停止运行5秒
    
        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
    
        cout << "end" << endl;
    }   
private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

执行上面的程序,如下图所示,发现另一个线程(代码未加入延时的线程)不会卡死,而是一直在执行其他任务,这就是使用try_to_lock的好处。
注意:使用try_to_lock不能在前面lock()

2.3)std::defer_lock参数
使用std::defer_lock的前提是也不能先执行lock(),否则会报出异常
defer_lock的意思就是初始化了一个没有加锁的mutex
相当于将unique_lock和mutex绑定在一起,用unique_lock管理mutex
借着defer_lock的话题,介绍一些unique_lock的重要成员函数,请看下文

三.unique_lock的成员函数

3.1)lock(),加锁
修改线程函数如下:

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;
            
            std::unique_lock<std::mutex> myUniqueLock(myMutex, std::defer_lock);  
            //创建没有加锁的myMutex,也不能提前lock()

            myUniqueLock.lock();          //注意不是mutex的成员函数lock(),且之后不用自己手动unlock()
        
            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
        }
        return;
    }

    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()

        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

使用修改后的线程函数,程序依然可以正常执行,注意此处的介绍的lock()函数并不是mutex类的成员函数,且之后不用自己手动unlock()(当然也可以自己手动unlock())

3.2)unlock()
疑惑:明明可以自动解锁,为什么还要有unlock()
解答:使用unlock()可以在程序中临时处理一些非共享代码,处理完之后再lock()。,lock()锁住的代码段越少,程序运行效率越高
当然,虽然上面的程序测试了使用lock()时不用自己手动unlock(),但也可以自己手动unlock()(画蛇添足
lock()锁住的代码段的多少称为锁的 粒度,粒度一般用粗细来描述
a)lock()锁住的代码,这个粒度叫,执行效率
b)lock()锁住的代码,这个粒度叫,执行效率
尽量选择合适粒度的代码进行保护,粒度太细,可能漏掉共享数据的保护,粒度太粗影响效率
修改后的线程函数如下:

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;

            std::unique_lock<std::mutex> myUniqueLock(myMutex, std::defer_lock);  
            //创建没有加锁的myMutex,也不能提前lock()

            myUniqueLock.lock();          //注意不是mutex的成员函数lock(),且之后不用自己手动unlock()
            //下方处理共享代码
            //...

            //因为有一些非共享代码要处理
            myUniqueLock.unlock();
            //下方处理非共享代码
            //...

            //处理完之后再进行lock()
            myUniqueLock.lock();
            //下方可处理共享代码
            //...

            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列

            myUniqueLock.unlock();        //画蛇添足,但也可以

        }
        return;
    }

    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()


        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }
private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

使用修改后的线程函数运行程序,演示了unlock()函数怎么用来临时处理非共享代码。
3.3)try_lock()
类似于mutex类try_to_lock()函数,尝试给互斥量加锁,如果拿不到锁(lock()失败),则返回false,否则返回true,这个函数是不阻塞的
修改后的线程函数如下:

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;

            std::unique_lock<std::mutex> myUniqueLock(myMutex, std::defer_lock); 
            //创建没有加锁的myMutex,也不能提前lock()

            if (myUniqueLock.try_lock())  //返回true表示达到锁(lock()成功)
            {
                msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
            }
            else
            {
                                         //返回false(lock()失败)
                cout << "inMsgRecvQueue()执行,但没有拿到锁" << i << endl;
            }

        }
        return;
    }
    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()

        std::chrono::milliseconds duration(5000);                           //5000毫秒=5秒
        std::this_thread::sleep_for(duration);                              //停止运行5秒


        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

上面的程序在其中一个线程延时了5秒,且还没有unlock(),另一个程序在这5秒之内无法获得锁(无法lock()成功),使用try_lock()不断尝试加锁,5秒过后可获得锁(lock()成功)。

3.4)release()
返回它所管理的mutex对象指针,并释放所有权,即unique_lock和mutex不再有关系
不要混淆unlock()和release()
如果原来mutex对象处于加锁状态,需要手动解锁
修改后的线程函数如下:

class A
{
public:
    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;

            std::unique_lock<std::mutex> myUniqueLock(myMutex);  //相当于mutex已经lock()
            std::mutex* ptrMutex = myUniqueLock.release();      
            //将unique_lock和mutex分离,现在需手动解锁mutex(unlock()),返回指向原来mutex的指针

            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
        
            ptrMutex->unlock();              //自己需手动unlock()

        }
        return;
    }

    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()

        std::chrono::milliseconds duration(5000);                           //5000毫秒=5秒
        std::this_thread::sleep_for(duration);                              //停止运行5秒


        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }

        cout << "end" << endl;
    }
private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

上述程序使用releas()分离了unique_lock和mutex后,后面需手动解锁(unlock())

四.unique_lock所有权的传递(一个unique_lock对应一个mutex)
std::unique_lock<std::mutex> myUniqueLock(myMutex);
所有权概念myUniqueLock拥有myMutex的所有权
myUniqueLock可以把自己对myMutex的所有权转移给其他的unique_lock对象
所有unique_lock对象的mutex的所有权可以转移,但是不能复制。类似于智能指针unique_ptr
下面修改的线程函数演示了所有权转移的方法,以及验证了所有权不能复制
a)使用std::move转移

class A
{
public:

    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;
    
            std::unique_lock<std::mutex> myUniqueLock1(myMutex);  //相当于mutex已经lock()
 
            //std::unique_lock<std::mutex> myUniqueLock2(myMutex); 
            //再使用myMutex创建一个unique_lock对象,会发现出错,相当于lock()两次,程序崩溃

            //std::unique_lock<std::mutex> myUniqueLock2(myUniqueLock); 
            //复制myMutex的所有权,会发现直接报错,此操作非法

            std::unique_lock<std::mutex> myUniqueLock2(std::move(myUniqueLock1));
            //将myMutex的所有权传递给myUniqueLock2,相当于myUniqueLock2与myMutex现在绑定在一起
            //而myUniqueLock1指向空,相当于release()

            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
        }
        return;
    }
    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()
    
        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }
    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
    
        cout << "end" << endl;
    }
private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

//b)return std::unique_lock<std::mutex>将临时对象拥有的所有权进行转移
下面修改的线程函数采取返回临时对象的方法转移所有权

class A
{
public:
    std::unique_lock<std::mutex> retUniqueLock()
    {
        std::unique_lock<std::mutex> tmpUniqueLock(myMutex);
        return tmpUniqueLock;      
        //返回一个局部的unique_lock对象是可以的,系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
    }

    //线程:把收到的消息(玩家命令)放入一个队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue(),插入一个元素" << i << endl;

            std::unique_lock<std::mutex> myUniqueLock = retUniqueLock();//所有权转移

            msgRecvQueue.push_back(i);    //假设i就是收到的命令,存入消息队列
        }
        return;
    }

    //判断消息是否为空,不为空取出命令
    bool outMsgProc(int& command)
    {
        myMutex.lock();
        std::unique_lock<std::mutex> myUniqueLock(myMutex, std::adopt_lock);
        //带std::adopt_lock参数必须提前lock()

        if (!msgRecvQueue.empty())
        {
            //消息不为空
            command = msgRecvQueue.front();             //返回第一个元素
            msgRecvQueue.pop_front();                   //移除第一个元素,但不返回
                                                        //处理取出的数据
            return true;
        }
        return false;
    }

    //线程:把收到的命令取出
    void outMsgRecvQueue()
    {
        int command;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outMsgProc(command);
            if (result)
            {
                cout << "outMsgRecvQueue()执行,取出一个命令" << command << endl;
                //处理命令
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }

        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;      //存放玩家发送的命令队列(共享数据)
    mutex myMutex;              //创建了一个互斥量(锁1)
};

相关文章

网友评论

      本文标题:多线程学习(五)

      本文链接:https://www.haomeiwen.com/subject/jthzzltx.html