文章目录
1、unique_lock取代lock_guard
unique_lock
- 是一个类模板,工作中,一般用
lock_guard
(推荐使用);lock_guard
取代了mutex
的lock()
和unlock()
; -
unique_lock
比lock_guard
灵活很多;效率上差一点,内存占用多一点。 - 常规使用,参数只有一个互斥量时,没有什么区别。
2、unique_lock第二个参数
2.1 std::adopt_lock
std::adopt_lock
-
std::adopt_lock
:表示这个互斥量已经被lock
了(使用前必须要把互斥量提前lock了,否则会报异常)。 -
std::adopt_lock
标记的效果就是“假设调用方线程已经拥有了互斥的所有权(就是已经lock()
成功了)。 - 通知
unique_lock
不需要在构造函数中lock()
这个互斥量了。
示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl;
my_mutex.lock(); //要先lock,后续才能用unique_lock的std::adopt_lock参数
std::unique_lock<std::mutex> sbguard1(my_mutex,std::adopt_lock);
msgRecvQueue.push_back(i);
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
2.2 std::try_to_lock
std::try_to_lock()
- 尝试用
mutex
的lock()
去锁定这个mutex
。但如果没有锁定成功,也会立即返回,并不会阻塞到那里。 - 用这个
try_to_lock
的前提是你自己不能先去lock
。
示例代码:
#include <iostream>
#include <thread>
#include <vector>
#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(my_mutex,std::try_to_lock);
if (sbguard1.owns_lock())
{
//拿到了锁
msgRecvQueue.push_back(i);
//......
}
else
{
cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
}
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
std::chrono::milliseconds dura(20000); //1s = 1000ms,20000ms = 20s
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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
2.3 std::defer_lock
std::defer_lock
- 用
std::defer_lock
的前提是,你不嗯能够自己先lock()
,否则会报异常。 -
defer_lock
的意思就是:并没有给mutex
加锁,初始化了一个没有加锁的mutex
。
3、unique_lock的成员函数
3.1 lock(),unlock()
示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> sbguard1(my_mutex, std::defer_lock); //没有加锁的my_mutex
sbguard1.lock(); //不用自己unlock
//因为有一些非共享的代码要处理,所以要unlock()
sbguard1.unlock();
//这里处理一些非共享代码
//处理完非共享代码后,继续上锁
sbguard1.lock();
//这里处理共享代码
//拿到了锁
msgRecvQueue.push_back(i);
//......
sbguard1.unlock(); //画蛇添足,但也可以
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
//std::chrono::milliseconds dura(20000); //1s = 1000ms,20000ms = 20s
//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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
3.2 try_lock())
try_lock()
- 尝试给互斥量加锁,如果拿不到锁,则返回
false
,如果拿到了锁,返回true
,这个函数不阻塞。
示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> sbguard1(my_mutex, std::defer_lock); //没有加锁的my_mutex
if (sbguard1.try_lock() == true) //返回true表示拿到锁了
{
msgRecvQueue.push_back(i);
cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
//......
}
else
{
cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
}
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s
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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
3.3 release()
release()
- 返回它所管理的
mutex
对象指针,并释放所有权;也就是说,这个unique_lock
和mutex
不再有关系。 - 严格区分
unlock()
和release()
的区别,不要混淆。 - 如果原来的
mutex
对象处于加锁状态,程序员有责任接管过来并负责解锁。
示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> sbguard1(my_mutex); //没有加锁的my_mutex
std::mutex* ptx = sbguard1.release(); //现在你有责任自己解锁这个my_mutex
msgRecvQueue.push_back(i);
cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
ptx->unlock(); //自己负责mutex的unlock()
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s
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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
为什么有时候需要
unlock()
?
- 因为
lock()
锁住的代码段越少,执行越快,整个程序运行效率越高。- 锁头锁住的代码的多少称为锁的粒度,粒度一般用粗细来描述。
- 锁住的代码少,这个粒度叫细,执行效率高。
- 锁住的代码多,粒度叫粗,执行效率就低。
- 要学会尽量选择合适粒度的代码进行保护,力度太细,可能漏掉共享数据的保护,粒度太粗,影响效率。
- 选择合适的粒度,是高级程序员的能力和实力的体现。
4、unique_lock所有权的传递
std::unique_lock<std::mutex> sbguard1(my_mutex)
-
sbguard1
拥有my_mutex
的所有权 -
sbguard1
可以把自己对mutex(my_mutex)
的所有权转移给其他的unique_lock
对象。 -
unique_lock
对象对mutex
的所有权可以转移,但不能复制。
4.1 std::move
第一种std::move
所有权传递示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A
{
public:
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> sbguard1(my_mutex); //没有加锁的my_mutex
std::unique_lock<std::mutex> sbguard2(std::move(sbguard1));
msgRecvQueue.push_back(i);
cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
//std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s
//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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
4.2 return std::unique_lockstd::mutex
第二种return std::unique_lock<std::mutex>
所有权传递示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A
{
public:
std::unique_lock<std::mutex> rtn_unique_lock()
{
std::unique_lock<std::mutex> tmpguard(my_mutex);
return tmpguard; //从函数返回一个局部的unique_lock对象是可以的
//返回这种局部对象tmp_guard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
}
//把收到的消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue()
{
for (int i = 0; i < 10000; i++)
{
std::unique_lock<std::mutex> tmpguard1 = rtn_unique_lock();
msgRecvQueue.push_back(i);
cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
}
return;
}
bool outMsgLULProc(int& command)
{
std::unique_lock<std::mutex> sbguard1(my_mutex);
//std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s
//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 result = outMsgLULProc(command);
if (result == true)
{
cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;
//接下来就考虑处理数据......
}
else
{
//消息队列为空
cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
}
}
}
private:
list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令
mutex my_mutex; //创建了一个互斥量
};
int main()
{
A myobja;
thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);
thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutnMsgObj.join();
cout << "I live China!" << endl; //最后执行这句,整个进程退出
system("pause");
return 0;
}
注:本人学习c++多线程视频地址:C++多线程学习地址