C++11多线程编程 6.unique_lock详解

#include<iostream>
#include<thread>
#include<vector>
#include<string>
#include<mutex>
#include<list>
using namespace std;
class A
{
public:
    //把收到的消息(玩家命令)入到一个队列的线程。
    void inMsgRecvQueue() {
        for (int i = 0; i < 10000; ++i) {
            cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
            /*std::lock_guard<std::mutex> sbguard(my_mutex1);*/

            //my_mutex1.lock();//要先lock,才能用下面的第二个参数。
            //std::unique_lock<std::mutex> sbguard(my_mutex1,std::adopt_lock);

            //std::unique_lock<std::mutex> sbguard(my_mutex1, std::try_to_lock);//用这个参数时候,前面不能加锁
                //同一个互斥量,如果其他线程锁住,并且卡住,如果不用这个参数,那么这个线程也会卡主,等待拿锁,
                //用了这个参数后,线程就不会卡住,先去执行其他的代码,然后再回过头继续判断能不能拿到锁 
            //if (sbguard.owns_lock()) {
                //拿到了锁

                //std::unique_lock<std::mutex> sbguard(my_mutex1, std::defer_lock);//用这个参数时候,前面不能加锁
                //sbguard.lock();//咱们不用自己lock,用了defer_lock后
                处理共享数据
                因为有一些非共享代码要处理,所以可以灵活的锁住,解锁。
                //sbguard.unlock();
                处理一些非共享代码数据,放开那些共享数据,要不然别的线程也无法访问共享数据
                //sbguard.lock();//再锁住,处理共享代码


                //if (sbguard.try_lock() == true) {
                    std::unique_lock<std::mutex> sbguard(my_mutex1);
                    std::mutex* ptr = sbguard.release();//解除sbguard和mutex的关系,ptr接收了和mutex的关系,所以要对其负责,
                                                        //现在你有责任自己解锁解锁这个my_mutex1;

                    msgRecvQueue.push_back(i);//假设数字i就是玩家收到的命令,我直接弄到消息队列里面,
                    ptr->unlock();//对应的是release解除关系之后的,ptr解锁
                    //其他处理代码
                //}
                //else
                //{
            //        cout << "没拿到锁" << endl;
            //    }
            //}
            //else {
        //        cout << "没拿到锁" << endl;
        //    }
            //...
            //其他代码
        }
        return;
    }

    bool outMsgLULProc(int& command) {
        //std::lock_guard<std::mutex> sbguard(my_mutex1);//sbguard是随便起的对象名
        std::unique_lock<std::mutex> sbguard(my_mutex1);
         
        std::chrono::milliseconds duar(200);//1秒=1000毫秒
        std::this_thread::sleep_for(duar);//让进程休息20秒,单位为毫秒

        if (!msgRecvQueue.empty()) {
            //消息队列不会空
            command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在
            msgRecvQueue.pop_front();//移除第一个元素。但不返回。
            return true;
        }
        return false;
    }

    //把数据从消息队列中取出的线程:
    void outMsgRecvQueue() {
        int command = 0;
        for (int i = 0; i < 10000; ++i) {
            bool result = outMsgLULProc(command);
            if (result == true) {
                cout << "outMsgRecvQueue执行了,取出一个元素,并且移除了一个元素" << command << endl;
                //可以进行数据处理 
                //....
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }
private:
    std::list<int>    msgRecvQueue;//容器,专门用于代表玩家给咱们发送的命令
    std::mutex my_mutex1;//创建了一个互斥量
};
int main()
{
    /*    (1)unique_lock取代lock_guard
    *            unique_lock是一个类模板,工作中lock_guard(推荐使用);lock_guard()取代了mutex::lock()和mutex::unlock()
    *            unique_lock比lock_guar更灵活,但是效率更高,内存占用多一点,可以完全取代lock_guard()
    *    (2)unique_lock的第二个参数
    *        (2.1)std::adopt_lock
    *            lock_guard()可以带第二个参数,std::lock_guard<std::mutex> sbguard(my_mutex1,std::adopt_lock());adopt_lock()起一个标记作用
    *             这个标记表示这个互斥量已经被lock()了(你必须把互斥量提前lock了,否则会报异常)
    *            这个标记的效果:假设调用方线程已经拥有了互斥的所有权(已经lock()成功了);
    *            通知lock_guard()不需要再构造函数中lock这个互斥量了,unique_lock()也可以带这个标记,含义相同,
    *            用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的重要成员函数。
    *    (3)unique_lock的成员函数
    *        (3.1)lock():手工加锁
    *        (3.1)unlock()可以灵活的处理共享数据和非共享数据,提高程序的灵活性
    *        (3.1)try_lock()尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到则返回true,这个函数不阻塞:
    *        (3.1)release(),返回他锁管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系
    *            严格区分release()和unlock的区别,不要混淆。
    *            如果原来mutex对象处于加锁状态,你有责任接管过来并负责解锁。(release返回的是原始的mutex指针,)
    *        (4)总结
    *            为什么有时候需要unlock(),因为你lock锁住的代码段越少,执行越快,整个系统的效率就越高
    *            有人也把锁头锁住的代码多少,成为锁的粒度,粒度一般用粗细描述。
    *                a)锁住的代码少,这个粒度叫细,执行效率高
    *                b)锁住的代码多,粒度叫粗,执行效率低
    *                要学会尽量选择合适粒度的代码进行保护,粒度太细,可能漏掉共享数据的保护,粒度太粗,影响效率、。
    *                选择合适的粒度,是高级程序员的能力和实力的体现。
    *    (4)unique_lock的所有权传递
    *            unique_lock一般和mutex绑定到一起发挥作用
    *            std::unique_lock<std::mutex> sbguard(my_mutex1);所有权概念,
    *            sbguard拥有my_mutex的所有权
    *            sbguard可以把自己对mutex(my_mutex)的所有权转移给其他的unique_lock对象
    *            所以unique_lock对象这个mutex的所有权是属于可以转移,但是不能复制,
                std::unique_lock<std::mutex> sbguard1(sbguard)复制所有权是非法的,std::unique_lock<std::mutex> sbguard1(std::move(sbguard))这是移动语义
                也可以通过返回临时对象来构造对象。
                std::unique_lock<std::mutex> rtn_unique_lock() {
                    std::unique_lock<std::mutex> tmpguard(my_mutex);
                    return tmpguard;
                }
                std::unique_lock<std::mutex> sbguard1=rtn_unique_lock();
    */            
    
    A myobj;
    std::thread mytobj2(&A::outMsgRecvQueue, &myobj);
    std::thread mytobj(&A::inMsgRecvQueue, &myobj);//第二个参数是引用,才能保证线程里用的是同一个对象。

    mytobj.join();
    mytobj2.join();

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值