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;
}
原文链接: https://www.cnblogs.com/lovebay/p/14430687.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/208344
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!