C++11------unique_lock

unique_lock

unique_lock独占的是mutex对象,就是对mutex锁的独占。
用法:
(1)新建一个unique_lock 对象
(2)给对象传入一个std::mutex 对象作为参数;

std::mutex mymutex;
unique_lock lock(mymutex);

因此加锁时新建一个对象lock,而这个对象生命周期结束后自动解锁。

#include <iostream>
#include<thread>
#include<unistd.h>
#include<mutex>
using namespace std;
std::mutex mymutex;
void sayHello()
{
    int k=0;
    unique_lock<mutex> lock(mymutex);
    while(k<2)
    {
        k++;
        cout<<endl<<"hello"<<endl;
        sleep(2);
    }
}
void sayWorld()
{
    unique_lock<mutex> lock(mymutex);
    while(1)
    {
         cout<<endl<<"world"<<endl;
         sleep(1);
    }
}
int main()
{
   thread threadHello(&sayHello);
   thread threadWorld(&sayWorld);
   threadHello.join();
   threadWorld.join();
   return 0;
}

std::unique_lock

       unique_lock也可以加std::adopt_lock参数,表示互斥量已经被lock,不需要再重复lock。该互斥量之前必须已经lock,才可以使用该参数。

std::try_to_lock

       可以避免一些不必要的等待,会判断当前mutex能否被lock,如果不能被lock,可以先去执行其他代码。这个和adopt不同,不需要自己提前加锁。举个例子来说就是如果有一个线程被lock,而且执行时间很长,那么另一个线程一般会被阻塞在那里,反而会造成时间的浪费。那么使用了try_to_lock后,如果被锁住了,它不会在那里阻塞等待,它可以先去执行其他没有被锁的代码。

#include "pch.h"
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A_Mutex {
public:
 
    void inMsgRecvQueue()  //把收到的消息(玩家命令)到一个队列的线程。100000次便于观察
    {
        for (int i = 0; i < 100000; i++)
        {
            cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
 
            {   //加上大括号可以让lock_guard提前(在处理其他代码前)解锁(超出作用域时(大括号)析构)
                //lock_guard <mutex> guard(my_mutex);
                //unique_lock <mutex> u_lock(my_mutex);  //unique_lock
 
                /*
                //try_to_lock
                unique_lock <mutex> u_lock_try(my_mutex,try_to_lock);
                if (u_lock_try.owns_lock())
                {
                    //拿到了锁
                    msgRecvQueue.push_back(i);
                    //....处理其他代码
                }
                else
                {
                    //没拿到锁
                    cout << "inMsgRecvQueue()执行,但没拿到锁,只能干别的事" << i << endl;
                }
                */
 
                unique_lock <mutex> u_lock_defer(my_mutex, defer_lock);   //没有加锁的my_mutex
 
 
                /* 
                //lock与unlock的使用
                u_lock_defer.lock();    //调用u_lock_defer的成员函数lock(),自带解锁,自己手工解锁也不会有问题(但画蛇添足)
                
                
                u_lock_defer.unlock(); //因为有一些非共享代码要处理,手工暂时解锁,体现灵活性
                //处理一些非共享代码
                u_lock_defer.lock(); //处理完后,再重新加锁
                //再次加锁后,再处理共享代码
                */
 
                //成员函数try_lock
                if (u_lock_defer.try_lock() == true)
                {
 
                    //拿到了锁
                    msgRecvQueue.push_back(i);
                    //....处理其他代码
                }
                else
                {
                    //没拿到锁
                    cout << "inMsgRecvQueue()执行,但没拿到锁,只能干别的事" << i << endl;
                }
 
                msgRecvQueue.push_back(i);
 
                //...处理代码
                //msgRecvQueue.push_back(i); //假设这个数字i就是我收到的命令,直接送到消息队列里去
                
            }
            //....处理其他代码
        }
        return;
    }
 
    bool outMsgLULProc(int &command)
    {
        //lock_guard <mutex> guard(my_mutex);   //guard为对象名,使用lock_guard时,lock与unlock不能再使用
        unique_lock <mutex> u_lock(my_mutex);  
        //std::chrono::milliseconds dura(20000); //20000毫秒==20秒
        //std::this_thread::sleep_for(dura);  //sleep 20s
        if (!msgRecvQueue.empty()) //消息不为空
        {
            command = msgRecvQueue.front();//返回第一个元素
            msgRecvQueue.pop_front();//弹出第一个元素(移除)
            
            return true;
        }
        return false;
 
    }
    void outMsgRecvQueue()//把数据从消息队列中取出的线程
    {
        int command = 0;
        for (int i = 0; i < 100000; i++)
        {
            if (outMsgLULProc(command) == true)
            {
                cout << "outMsgRecvQueue()执行" << i << endl;
                //可以对command进行数据处理
            }
            else
            {
                //消息队列为空
                cout << "outMsgRecvQueue()执行,但目前消息队列为空" << i << endl;
            }
        }
        cout << "end" << endl;
        return;
    }
private:
    list <int> msgRecvQueue; //容器(消息队列),专门用于代表玩家发送过来的命令
    mutex my_mutex; //创建了一个互斥量(一个锁)
 
};
 
int main()
{
    A_Mutex myobj_mutex;
    thread myOutnMsgObj(&A_Mutex::outMsgRecvQueue, &myobj_mutex);  //第二参数是引用(就无需拷贝),才能保证线程里用的是同一对象
    thread myInMsgobj(&A_Mutex::inMsgRecvQueue, &myobj_mutex);
    myOutnMsgObj.join();
    myInMsgobj.join();
 
    //一:unique_lock取代lock_guard :  unique_lock <mutex> u_lock(my_mutex); 取代  lock_guard <mutex> guard(my_mutex);
    //unique_lock是个类模板。工作中,一般lock_guard()足够了,推荐使用。
    //unique_lock比lock_guard灵活很多;效率上差一点,内存占用多一点
 
    //二:unique_lock的第二个参数
    
    //(2.1)std::adopt_lock
    //用adopt_lock的前提是互斥量已被lock,通知lock_guard的构造函数无需再lock
    //lock_guard的第二个参数:lock_guard <mutex> guard(my_mutex,std::adopt_lock);   //adopt_lock 起标记作用
    //unique_lock也可以带std::adopt_lock参数,含义相同
    
    //(2.2)std::try_to_lock
    //会尝试用mutex的lock去锁定这个mutex,但如果没有锁定成功,也会立即返回,并不会一直阻塞
    //用try_to_lock的前提是不能自己手动去lock
 
    //(2.3)std::defer_lock
    //用defer_lock的前提是不能自己手动去lock(同try_to_lock)
    //使用它并不会给mutex加锁,作用是初始化了一个没有加锁的mutex
 
    //三:unique_lock的成员函数
    //(3.1)lock(),加锁
    //(3.2)unlock(),解锁,虽然成员函数lock()自带解锁,但unlock可以更加灵活(比如有一些非共享代码要处理:可以暂时解锁再加锁)
    //(3.3)try_lock(),尝试给互斥量加锁,如果拿不到锁,则返回false(不阻塞),如果拿到了锁,返回true。
    //(3.4)release(),返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系
    //如果原来mutex对象处于加锁状态,需接管过来并负责解锁
 
    //lock锁住的代码段越少,执行越快,整个程序运行效率越高
    //锁住的代码的多少称为锁的 粒度。粒度一般用粗细来描述
    //选择合适的粒度,是高级程序员的能力和实力的体现
 
    //四:unique_lock所有权的传递 
    //unique_lock <mutex> u_lock_defer(my_mutex);    所有权概念: u_lock_defer拥有my_mutex的所有权
    //u_lock_defer可以把自己对my_mutex的所有权 转移 给其他的unique_lock对象,但是不能 复制
    //转移:unique_lock <mutex> u_lock_defer2(std::move(u_lock_defer));    //移动语义,相当于u_lock_defer2与my_mutex绑定在一起了。u_lock_defer则指向空,u_lock_defer2指向空 
 
 
    cout << "主线程执行完毕" << endl; //执行完这句,主线程退出
    return 0; 
}

 

posted @ 2021-02-22 15:41  玥茹苟  阅读(3064)  评论(0编辑  收藏  举报