C++并行开发6-unique_lock详解.md

FengLY Lv3

一、 unique_lock 取代lock_guard

adopt_lock

表示这个互斥量已经被lock了,必须把互斥量提前lock,否则会报异常

try_to_lock

当outMsgRecgQueue线程有一个sleep的时候,会导致另一个inMsgRecvQueue线程卡死,尝试使用mutex的lock()
去锁定这个互斥量,但是如果没有锁定成功,也会立即返回,并不会阻塞在那里

#include <iostream>
#include <thread>
#include <algorithm>
#include <vector>
#include <list>
#include <mutex>
using namespace std;

//调用类对象的成员函数的方式来创建线程
class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++) {
            //std::lock_guard<std::mutex> sbguard1(my_mutex1);  //前面已经锁过了,这里需要adopt_lock
            /*std::unique_lock<std::mutex> sbguard1(my_mutex1);  
            cout << "消息队列插入一个元素 " << i << endl;
            msgRecvQueue.push_back(i);*/
            std::unique_lock<std::mutex> sbguard1(my_mutex1, std::try_to_lock);  //尝试拿到锁头
            if (sbguard1.owns_lock()) {
                msgRecvQueue.push_back(i);
            }
            else {
                cout << "inMsgRecvQueue 执行但是没有拿到锁头, 干点其他的事" << endl;
            }
        }
    }

    bool judgeOutMsg(int &command) {
        //std::lock_guard<std::mutex> sbguard1(my_mutex1);  
        //将lock_guard 取代成unique_lock
        std::unique_lock<std::mutex> sbguard1(my_mutex1);  

        std::chrono::milliseconds dura(20000);   //20s
        std::this_thread::sleep_for(dura);   //休息20s  这个线程休息20s,导致下一个线程因为互斥量也休息了20s

        if (!msgRecvQueue.empty()) {
            //消息队列非空
            command = msgRecvQueue.front();  //取出第一个元素
            msgRecvQueue.pop_front();
            return true;
        }
        else {
            return false;
        }
    }

    void outMsgRecgQueue() {
        int commond = 0;
        for (int i = 0; i < 10000; i++) {
            bool result = judgeOutMsg(commond);
            if (result) {
                cout << "消息队列中取出了一个元素 " << commond << endl;
            }
            else {
                cout << "消息队列为空 " << endl;
            }
        }
    }

private:
    list<int> msgRecvQueue;  //消息队列
    std::mutex my_mutex1;// 创建一个互斥量
    std::mutex my_mutex2;// 创建一个互斥量
};



int main()
{
    A myobja;
    std::thread myOutnMsgObj(&A::outMsgRecgQueue, &myobja);
    std::thread myInnMsgObj(&A::inMsgRecvQueue, &myobja);

    //这里必须使用引用,否则线程会重新拷贝构造一个对象,
    //但是使用引用就需要考虑类对象的使用周期,必须在主线程使用结束

    myOutnMsgObj.join();
    myInnMsgObj.join();

    //一、 unique_lock 取代lock_guard
    // unique_lock 是一个类模板, 比lock_guard灵活很多,但是内存,效率差一些
    // 1.1 std::adopt_lock :表示这个互斥量已经被lock了,必须把互斥量提前lock,否则会报异常
    // 1.2 std::try_to_lock
    // 尝试使用mutex的lock() 去锁定这个互斥量,但是如果没有锁定成功,也会立即返回,并不会阻塞在那里
    // 用这个try_to_lock的前提是自己不能去lock
    return 0;
}

std::defer_lock

主要用于成员函数中

2. unique_lock的一些成员函数

2.1 lock() 加锁 && 2.2 unlock()

因为有一些非共享数据要处理,所有要处理一些非共享数据,因此可以人为的加lock或者unlock

#include <iostream>
#include <thread>
#include <algorithm>
#include <vector>
#include <list>
#include <mutex>
using namespace std;

//调用类对象的成员函数的方式来创建线程
class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++) {
            //std::lock_guard<std::mutex> sbguard1(my_mutex1);  //前面已经锁过了,这里需要adopt_lock
            /*std::unique_lock<std::mutex> sbguard1(my_mutex1);  
            cout << "消息队列插入一个元素 " << i << endl;
            msgRecvQueue.push_back(i);*/
            //std::unique_lock<std::mutex> sbguard1(my_mutex1, std::try_to_lock);  //尝试拿到锁头
            //if (sbguard1.owns_lock()) {
            //  msgRecvQueue.push_back(i);
            //}
            //else {
            //  cout << "inMsgRecvQueue 执行但是没有拿到锁头, 干点其他的事" << endl;
            //}
            std::unique_lock<std::mutex> sbguard1(my_mutex1, std::defer_lock);
            sbguard1.lock();  //不用担心解锁,这里会自动解锁
            msgRecvQueue.push_back(i);
        }
    }

    bool judgeOutMsg(int &command) {
        //std::lock_guard<std::mutex> sbguard1(my_mutex1);  
        //将lock_guard 取代成unique_lock
        std::unique_lock<std::mutex> sbguard1(my_mutex1);  

        //std::chrono::milliseconds dura(20000);   //20s
        //std::this_thread::sleep_for(dura);   //休息20s  这个线程休息20s,导致下一个线程因为互斥量也休息了20s

        if (!msgRecvQueue.empty()) {
            //消息队列非空
            command = msgRecvQueue.front();  //取出第一个元素
            msgRecvQueue.pop_front();
            return true;
        }
        else {
            return false;
        }
    }

    void outMsgRecgQueue() {
        int commond = 0;
        for (int i = 0; i < 10000; i++) {
            bool result = judgeOutMsg(commond);
            if (result) {
                cout << "消息队列中取出了一个元素 " << commond << endl;
            }
            else {
                cout << "消息队列为空 " << endl;
            }
        }
    }

private:
    list<int> msgRecvQueue;  //消息队列
    std::mutex my_mutex1;// 创建一个互斥量
    std::mutex my_mutex2;// 创建一个互斥量
};



int main()
{
    A myobja;
    std::thread myOutnMsgObj(&A::outMsgRecgQueue, &myobja);
    std::thread myInnMsgObj(&A::inMsgRecvQueue, &myobja);

    //这里必须使用引用,否则线程会重新拷贝构造一个对象,
    //但是使用引用就需要考虑类对象的使用周期,必须在主线程使用结束

    myOutnMsgObj.join();
    myInnMsgObj.join();

    //一、 unique_lock 取代lock_guard
    // unique_lock 是一个类模板, 比lock_guard灵活很多,但是内存,效率差一些
    // 1.1 std::adopt_lock :表示这个互斥量已经被lock了,必须把互斥量提前lock,否则会报异常
    // 1.2 std::try_to_lock
    // 尝试使用mutex的lock() 去锁定这个互斥量,但是如果没有锁定成功,也会立即返回,并不会阻塞在那里
    // 用这个try_to_lock的前提是自己不能去lock
    // 1.3 std::defer_lock
    // 用这个defer_lock的前提是你自己不能先lock,否则会报异常
    // defer_lock的意思是并没有给mutex加锁,初始化了一个没有加锁的mutex

    // 2. unique_lock的一些成员函数
    // 2.1 lock()  加锁
    // 2.2 unlock()  因为有一些非共享数据要处理,所有要处理一些非共享数据

    return 0;
}

try_lock()

尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到锁,返回true,这个函数不堵塞,类似try_to_lock

void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++) {
            //std::lock_guard<std::mutex> sbguard1(my_mutex1);  //前面已经锁过了,这里需要adopt_lock
            /*std::unique_lock<std::mutex> sbguard1(my_mutex1);  
            cout << "消息队列插入一个元素 " << i << endl;
            msgRecvQueue.push_back(i);*/
            //std::unique_lock<std::mutex> sbguard1(my_mutex1, std::try_to_lock);  //尝试拿到锁头
            //if (sbguard1.owns_lock()) {
            //  msgRecvQueue.push_back(i);
            //}
            //else {
            //  cout << "inMsgRecvQueue 执行但是没有拿到锁头, 干点其他的事" << endl;
            //}
            std::unique_lock<std::mutex> sbguard1(my_mutex1, std::defer_lock);
            //sbguard1.lock();  //不用担心解锁,这里会自动解锁

            if (sbguard1.try_lock()) {
                msgRecvQueue.push_back(i);
            }
            else {
                cout << "inMsgRecvQueue 执行但是没有拿到锁头, 干点其他的事" << endl;
            }


        }
    }

release函数

锁住代码的多少称为锁头的粒度

void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++) {
            std::unique_lock<std::mutex> sbguard1(my_mutex1);
            std::mutex *ptx = sbguard1.release();  //可以接管mutex

            //ptx->lock();  //这里不能再lock 了因为前面已经lock 过了
            msgRecvQueue.push_back(i);
            ptx->unlock();
        }
    }

unique_lock所有权的传递

使用移动构造函数可以使所有权进行转移

std::unique_lock<std::mutex> sbguard1(my_mutex1);
std::unique_lock<std::mutex> sbguard2(std::move(sbguard1));  //使用移动构造函数 sbguard2 和 my_mutex1绑定一起了,sbguard1指向空 

也可以写一个类中的函数进行所有权转移 F:/网站备份-20221127/zhouaq_backup_20221127.com/zhouaq.com/wp-content/p://zhouaq.com/wp-./uploads/2020/10/wp_editor_md_e554cff01d898b6df220102aa7c84b22.jpg)](http://zhouaq.com/wp-
content/uploads/2020/10/wp_editor_md_e554cff01d898b6df220102aa7c84b22.jpg)

  • Title: C++并行开发6-unique_lock详解.md
  • Author: FengLY
  • Created at : 2023-06-18 22:40:09
  • Updated at : 2023-06-18 22:57:07
  • Link: https://zhouaq.com/2023/06/18/C++并行开发6-unique_lock详解/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments