zoukankan      html  css  js  c++  java
  • 六、unique_lock取代lock_guard

    unique是个类模板,工作中,一般lock_guard(推荐使用);

    lock_guard取代了mutex的 lock() 和 unlock();unique_lock又取代lock_guard;qaq

    unique和guard都是RAII风格的机制来的机制在作用域块的持续时间内拥有一个互斥量

    • 创建lock_guard对象时,它将获取提供给他的互斥锁的所有权,当控制流离开lock_guard对象的作用域时,lock_guard析构并释放互斥量,
      • 但是不能途中解锁。
      • 创建即锁定
    • unique_lock允许锁定包装器,它允许延迟锁定,限时深度锁定,递归锁定以及与条件变量一起使用,具有lock_guard的所有功能,能够应对更复杂的锁定需要。其有如下特点:
      • 创建时可不锁定(指定第二个参数为std::defer_lock),而在需要时再锁定。
      • 可以随时加锁解锁
      • 作用域同lock_guard,析构时自动释放锁
      • 不可复制,可移动
      • 条件变量需要该类型作为参数。

    unique_lock比lock_guard灵活很多,效率上差一些,内存占用多一些。

    #include <thread>
    #include <iostream>
    #include <list>
    #include <mutex>
    using namespace std;
     
    class A {
    public:
        void inMsgRecvQueue() {
            for (int i = 0; i < 10000; ++i) {
                cout << "inMsgRecvQueue插入一个元素" << i << endl;
                std::lock(mymutex1, mymutex2);
                std::unique_lock<std::mutex> sbguard1(mymutex1, std::adopt_lock);
                //std::lock_guard<std::mutex> sbguard2(mymutex2, std::adopt_lock);
                msgRecvQueue.push_back(i);
                //mymutex2.unlock();
                //...其他操作
                //mymutex1.unlock();
            }
        }
     
        bool outMsgProc(int &command) {
            std::lock(mymutex1, mymutex2);
            std::unique_lock<std::mutex> sbguard1(mymutex1, std::adopt_lock);
            //std::lock_guard<std::mutex> sbguard2(mymutex2, std::adopt_lock);
            if (!msgRecvQueue.empty()) {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                //mymutex1.unlock();
                //mymutex2.unlock();
                return true;
            }
            //mymutex1.unlock();
            //mymutex2.unlock();
            return false;
        }
     
        void outMsgRecvQueue() {
            int command = 0;
            for (int i = 0; i < 10000; ++i) {
                bool re = outMsgProc(command);
                if (re == true) {
                    cout << "outMsgRecvQueue执行,取出一个元素" << command << endl;
                }
                else {
                    cout << "消息队列为空" << endl;
                }
            }
        }
     
    private:
        std::list<int> msgRecvQueue;
        std::mutex mymutex1;
        std::mutex mymutex2;
    };
     
    int main() {
        A myobja;
        std::thread myoutobj(&A::outMsgRecvQueue, &myobja); //注意这里myobja用引用,才能保证线程里用的是同一个对象
        std::thread myinobj(&A::inMsgRecvQueue, &myobja);
        myinobj.join();
        myoutobj.join();
    }

    总结:能用lock_guard就用它,它不行就用unique_lock。

    unique_lock可以带第二个参数

    1、std::adopt_lock:表示互斥量已经被lock了(程序员必须把互斥量先lock,否则会报异常)

    效果:假设线程调用方赢拥有了互斥的所有权,lock_graud与unique_lock都可以加这个参数,意义相同。

    2、std::try_to_lock

    我们会尝试用mutex中的lock()去锁定这个mutex,但如果没有锁定成功,也会立刻返回,并不会阻塞

    #include <thread>
    #include <iostream>
    #include <list>
    #include <mutex>
    using namespace std;
     
    class A {
    public:
        void inMsgRecvQueue() {
            for (int i = 0; i < 10000; ++i) {
                cout << "inMsgRecvQueue插入一个元素" << i << endl;
                
                std::unique_lock<std::mutex> sbguard1(mymutex1, std::try_to_lock);
                if (sbguard1.owns_lock()) {  //判断是否拿到了锁
                    msgRecvQueue.push_back(i);  //拿到了锁
                }
                else {
                    cout << "没能拿到锁" << endl;
                }
            }
        }
     
        bool outMsgProc(int &command) {
            mymutex1.lock();
            std::unique_lock<std::mutex> sbguard1(mymutex1, std::adopt_lock);
            //std::lock_guard<std::mutex> sbguard2(mymutex2, std::adopt_lock);
     
            std::chrono::milliseconds dura(20000);
            std::this_thread::sleep_for(dura);
     
            if (!msgRecvQueue.empty()) {
                command = msgRecvQueue.front();
                msgRecvQueue.pop_front();
                return true;
            }
            return false;
        }private:
        std::list<int> msgRecvQueue;
        std::mutex mymutex1;
    };
     

    通过这种方式让线程在没有拿到锁时执行其他事情

    3、std::defer_lock:前提是你不能先lock(),否则汇报异常

      defer_lock的意思是并没有给mutex枷锁:初始化了一个没有加锁的mutex

    unique的成员函数(与defer_lock配合使用)

    1、lock():加锁,加锁后,不用必须加unlock,unique_lock会帮助我们unlock()

    2、unlock():解锁,如果想自己解锁,也可以使用unlock()

    为什么需要unlock(),因为你锁住的代码段越少,执行越快,整个程序效率就越高。

    也有人把搜头锁住的代码多少称为 粒度。一般用粗细描述。

    a、锁住的代码少,粒度细;b、锁住的代码多粒度粗

    3、try_lock():尝试给互斥量加锁,如果拿不到锁返回false,如果拿到了锁,返回true

    4、release():返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系

    如果原来mutex对象处于加锁状态

    std::mutex mymutex1;
     
    std::unique_lock<std::mutex> myuniquemutex(mymutex1);
     
     
    std::mutex *pmutex = myuniquemutex.release();
    //此时,你就需要自己unlock()了

    如果myuniquemutex还有锁没被释放,那pmutex需要负责释放。

    unique_lock所有权传递

    std::mutex mymutex1;
    std::unique_lock<std::mutex> sbguard1(mymutex1);

    此时,sbguard1拥有mymutex1的所有权,

    std::unique_lock<std::mutex> sbguard2(sbguard1); //非法
    std::unique_lock<std::mutex> sbguard2(std::move(sbguard1)); //移动语义,现在相当于sbguard2与mymutex1绑定到一起了
    class Test{
    public:
        std::mutex mymutex1;
        std::uniuqe_lock<std::mutex> rtn_guard(){
        
            std::unique_lock<std::mutex> tmpmutex(mymutex1);
            return tmpmutex; //因为tmpmutex为局部变量,函数返回局部变量时,会产生一个临 
                             //时对象,并调用移动构造函数传递给外部变量
        }
        std::uniuqe_lock<std::mutex> sbguard1 = rtn_guard();
    }
    心之所愿,永不相忘
  • 相关阅读:
    架构设计中的方法学——迭代设计
    架构重构改善既有代码的设计
    架构如何为业务和技术“服务”(2)
    架构如何为业务和技术“服务”(1)
    选用面向领域的多层分布式架构(DDD风格架构)的理由
    传话游戏(2013编程之美全国挑战赛资格赛)
    2012年蓝桥杯软件设计大赛河南省初赛试题和答案
    最大值最下化
    NYOJ120 校园网络(强连通缩点targan算法)
    NYOJ176整数划分(二)
  • 原文地址:https://www.cnblogs.com/zgll/p/15291467.html
Copyright © 2011-2022 走看看