并发编程6——unique_lock

目录

一、unique_lock取代lock_guard

  • unique_lock可以完全取代lock_guard
  • unique_lock是一个类模板,比lock_guard灵活,但是效率差一些

二、unique_lock的第二个参数

2.1 std::adopt_lock参数

  • 该参数表示这个当前这个互斥量mutex已经被lock了,那么就不要再调用ctor中的lock()函数了,只调用析构函数中的unlock()就好。

要先给mutex加上lock,然后才能调用unique_lock中的adopt_lock参数

2.2 std::try_to_lock

因为in线程out线程都有可能先被调用,我们在out线程加了一段代码,让其等待20s,也就是给my_mutex1加上lock以后,等待了20S,这样会导致in线程拿不到mymutex也跟着等待20S,所以我们引入了try_to_lock

但是和adopt_lock不同的是,try_to_lock使用的时候一定不能先lock(),不然就会直接卡住——

卡住

然后报错


异常

因为我们引用try_to_lock的意义是,当我们使用lock去锁定mutex的时候,如果没有锁定成功,会立即返回,而不是阻塞(等待)在那里。

所以用try_to_lock的时候一定不能先去lock
try_to_lock的意思是尝试加锁

它的好处就是——当线程A拿不到锁的时候,线程A不会等在那里,它会去做一些别的事情

2.3 defer_lock

使用defer_lock的前提是——不能自己先lock,不然会有异常。(跟try_to_lock一样)

defer_lock并没有给mutex加锁

  • 初始化了一个没有加锁的mutex(从而增加了灵活性)

三、unique_lock的成员函数

3.1 lock()

  • 加锁

3.2 unlock()

  • 解锁
    因为只有共享代码需要加锁,然后解锁,非共享代码是不需要的。
    所以上述中,我们先用了defer_lock,然后调用了lock(),并没有调用unlock(),这样会导致有一部分非共享代码此时是被锁住的,也就不能执行的,但是锁住非共享代码是没必要的,所以我们可以调用unlock(),从而让非共享代码可以及时处理。当我们处理完这些非共享代码后,可以再调用lock()函数对共享代码进行上锁。

unique_lock()的灵活性和稳定性体现在2点——

  • 可以随时处理共享或者非共享的代码
  • 上锁后如果不着急处理非共享的代码,可以只调用一个lock()

defer_lock的使用相当于将go_guard1和unique_lock和mutex三者绑定在了一起

lock是给unique_lock绑定的mutex加锁

3.3 try_lock()

尝试给互斥量加锁

  • 如果拿不到锁,则返回false
  • 如果拿到了锁,则返回true
  • 这个函数是不阻塞的

3.4 relese()

返回管理的Mutex对象指针, 并释放所有权,从而实现unique_lockmutex不再有关系。

  • 本来是把unique_lock和mutex绑定在一起,relese就是把它俩分开了(好残忍呀~),也就是说,relese是放弃了unique_lock和mutex之间的关系。

  • 所以说,如果之前mutex对象属于加锁状态,那么relese后,我需要负责解锁

release返回的是原始的mutex指针,代码中的是*ptx,对应的就是mutex对象。

3.5 锁的粒度

  • 锁头锁住的代码的多少称为粒度
  • 锁住的代码少,粒度就比较细,程序的运行效率就高
  • 锁住的代码多,粒度就比较粗,程序的运行效率就低

四、unique_lock的所有权的传递

  •  std::unique_lock<std::mutex> go_guard1(my_mutex1);
    

可以说go_guard1拥有my_mutex1的所有权
go_guard1可以把自己对mutex的所有权转移给其他的unique_lock对象,比如go_guard2

unique_lock对象对于mutex的所有权是属于可以转移,但是不能复制。转移的话使用move

  • std::unique_lock<std::mutex> go_guard2(std::move(go_guard1));
    

转移的话也可以使用函数的临时对象——

  •  return rtn_unique_lock()
    

源代码

  • try_to_lock
#include <iostream>
#include <vector>
#include <thread>
#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(my_mutex1, my_mutex2); // 要先给mutex加上lock
                                             // 然后才能调用unique_lock中的adopt_lock参数

            /*my_mutex1.lock();
            std::unique_lock<std::mutex> go_guard1(my_mutex1, std::adopt_lock);*/

            std::unique_lock<std::mutex> go_guard1(my_mutex1, std::try_to_lock);
             
            if (go_guard1.owns_lock()) msgRecvQueue.push_back(i);
            else cout << "inMsgRecvQueue()执行,但是没有拿到锁" << i << endl;
        }

        return;
    }

    bool outMsgLULProc(int& command)
    {
        std::unique_lock<std::mutex> go_guard1(my_mutex1);

        std::chrono::milliseconds dura(200);
        std::this_thread::sleep_for(dura);

        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 res = outMsgLULProc(command);

            if (res == true) cout << "outMsgRecvQueue执行,取出一个元素" << command << endl;
            else cout << "outMsgRecvQueue()执行,但是目前消息队列中为空" << i << endl;
        }

        cout << "end" << endl;
    }

private:
    std::list<int> msgRecvQueue; 
    std::mutex my_mutex1;   
    std::mutex my_mutex2;
};


int main()
{
    A myobj_a;

    std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobj_a);
    std::thread myInMsgObj(&A::inMsgRecvQueue, &myobj_a);

    myInMsgObj.join();
    myOutMsgObj.join();
}

  • unique_lock的lock()函数
#include <iostream>
#include <vector>
#include <thread>
#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> go_guard1(my_mutex1, std::defer_lock); // 没有加锁的mutex
            go_guard1.lock();   // 不用自己unlock,因为unique_lock是智能的
                                // 而且这个lock()函数是guard调用的,不是mutex调用的
             
            msgRecvQueue.push_back(i);
        }

        return;
    }

    bool outMsgLULProc(int& command)
    {
        std::unique_lock<std::mutex> go_guard1(my_mutex1);

        std::chrono::milliseconds dura(200);
        std::this_thread::sleep_for(dura);

        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 res = outMsgLULProc(command);

            if (res == true) cout << "outMsgRecvQueue执行,取出一个元素" << command << endl;
            else cout << "outMsgRecvQueue()执行,但是目前消息队列中为空" << i << endl;
        }

        cout << "end" << endl;
    }

private:
    std::list<int> msgRecvQueue; 
    std::mutex my_mutex1;   
    std::mutex my_mutex2;
};


int main()
{
    A myobj_a;

    std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobj_a);
    std::thread myInMsgObj(&A::inMsgRecvQueue, &myobj_a);

    myInMsgObj.join();
    myOutMsgObj.join();
}
  • defer_lock的成员函数lock()和unlock()并用
#include <iostream>
#include <vector>
#include <thread>
#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> go_guard1(my_mutex1, std::defer_lock); // 没有加锁的mutex
            
            go_guard1.lock();   // 不用自己unlock,因为unique_lock是智能的
                                // 而且这个lock()函数是guard调用的,不是mutex调用的
            go_guard1.unlock();

            go_guard1.lock();
             
            msgRecvQueue.push_back(i);
        }

        return;
    }

    bool outMsgLULProc(int& command)
    {
        std::unique_lock<std::mutex> go_guard1(my_mutex1);

        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 res = outMsgLULProc(command);

            if (res == true) cout << "outMsgRecvQueue执行,取出一个元素" << command << endl;
            else cout << "outMsgRecvQueue()执行,但是目前消息队列中为空" << i << endl;
        }

        cout << "end" << endl;
    }

private:
    std::list<int> msgRecvQueue; 
    std::mutex my_mutex1;   
    std::mutex my_mutex2;
};


int main()
{
    A myobj_a;

    std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobj_a);
    std::thread myInMsgObj(&A::inMsgRecvQueue, &myobj_a);

    myInMsgObj.join();
    myOutMsgObj.join();
}
  • try_lock()
#include <iostream>
#include <vector>
#include <thread>
#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> go_guard1(my_mutex1, std::defer_lock); // 没有加锁的mutex
            
            if (go_guard1.try_lock()) msgRecvQueue.push_back(i);
            else cout << "inMsgRecvQueue()执行,但是没有拿到锁,只能做一些别的事情" << i << endl;
        }

        return;
    }

    bool outMsgLULProc(int& command)
    {
        std::unique_lock<std::mutex> go_guard1(my_mutex1);

        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 res = outMsgLULProc(command);

            if (res == true) cout << "outMsgRecvQueue执行,取出一个元素" << command << endl;
            else cout << "outMsgRecvQueue()执行,但是目前消息队列中为空" << i << endl;
        }

        cout << "end" << endl;
    }

private:
    std::list<int> msgRecvQueue; 
    std::mutex my_mutex1;   
    std::mutex my_mutex2;
};


int main()
{
    A myobj_a;

    std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobj_a);
    std::thread myInMsgObj(&A::inMsgRecvQueue, &myobj_a);

    myInMsgObj.join();
    myOutMsgObj.join();
}
  • relese()
void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++)
        {
            cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

            std::unique_lock<std::mutex> go_guard1(my_mutex1);
            std::mutex* ptx = go_guard1.release();
            
            msgRecvQueue.push_back(i);

            ptx->unlock();
        }

        return;
    }
  • 所有权转移
void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++)
        {
            cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

            std::unique_lock<std::mutex> go_guard1(my_mutex1);
            std::unique_lock<std::mutex> go_guard2(std::move(go_guard1));
            
            msgRecvQueue.push_back(i);
        }

        return;
    }
  • return unique_lock临时对象
class A
{
public:

    // 从函数返回一个局部的unique_lock对象是可以的,返回局部对象会让系统生成一个临时unique_lock对象
    // 并调用unique_lock的移动构造函数
    std::unique_lock<std::mutex> rtn_unique_lock()
    {
        std::unique_lock<std::mutex> tmpguard(my_mutex1);
        return tmpguard;
    }

    void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++)
        {
            cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

            std::unique_lock<std::mutex> go_guard1 = rtn_unique_lock();
            
            msgRecvQueue.push_back(i);
        }

        return;
    }
...
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容