1、unique_lock介绍
(1)unique_lock是个类模板,它比lock_guard灵活很多,但是效率会低一些,占用内存较多;一般推荐使用lock_guard(lock_guard取代了mutex中的lock()和unlock());
2、unique_lock<std::mutex> 使用的灵活性
2.1 unique_lock与lock_guard使用对比
std::lock_guard<std::mutex> uniqueLock(m_Mutex, std::adopt_lock);
// adopt_lock标记作用,表示互斥量已经被lock了(互斥量必须提前lock,否则会出现异常),lock_guard不需要在构造函数中lock这个互斥量
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::adopt_lock);
// unique_lock也可以带adopt_lock,含义相同
// unique_lock()使用的灵活性
std::unique_lock<std::mutex> uniqueLock(m_Mutex,std::defer_lock); // 没有加锁的m_Mutex
m_Mutex.lock(); // 多次重复调用时需要自己解锁,后再加锁
// 处理共享内存
m_Mutex.unlock();
// 处理非共享内存
m_Mutex.lock();
// 再次处理共享内存
m_Mutex.unlock();
3、unique_lock其他参数
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::try_to_lock); // 参数使用范例
try_to_lock参数,类似于lock()去锁定这个互斥量,如果没有锁成功,就会立即返回,不会出现阻塞现象,使用try_to_lock的前提是不能在前面使用lock(),添加后需要unlock
std::unique_lock<std::mutex>
std::uniqueLock(m_Mutex, std::defect_lock); // 参数使用范例
defect_lock参数,前提是不能在前面添加lock,否则会报异常;defer_lock作用是,初始化一个未加锁的mutex
// 存数据
void inMsgRecvQueue()
{
for (int i = 0; i < 1000; i++)
{
// 添加作用域
{
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::try_to_lock);
if (uniqueLock.owns_lock())
{
m_RecvQueue.push_back(i);
}
else
{
cout << "没有获得锁" << endl;
}
}
// doSomething
// ...
}
}
4、unipue_lock成员函数
lock,unlock 加锁,解锁
try_lock,尝试给互斥量加锁,如果拿不到锁,返回false,否则返回true,不会导致函数阻塞
release,返回它所管理的mutex对象指针,并释放所有权,使unipue_lock与mutex不再有关系
如果mutex对象处于加锁状态,此指针负责解锁
bool outMsgQueue(int &num)
{
// try_lock()用法
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::defer_lock); // 没有加锁的m_Mutex
if (uniqueLock.try_lock() == true)
{
if (!m_RecvQueue.empty())
{
num = m_RecvQueue.front(); // 返回第一个元素,但是不检查元素是否存在;
m_RecvQueue.pop_front(); // 移除第一个元素但不反回
//m_Mutex.unlock();
return true;
}
}
else
{
cout << "没有获得锁" << endl;
return false;
}
}
// 取数据
void outMsgRecvQueue()
{
int num;
for (int i = 0; i < 1000; i++)
{
if (outMsgQueue(num))
{
cout << "delet one data" << num << endl;
}
else
{
cout << "outMsgRecvQueue no data" << endl;
}
}
cout << "end" << endl;
}
5、unipue_lock所有权的传递
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::defer_lock)
所有权:uniqueLock拥有m_Mutex的所有权
unique_lock的mutex对象的所有权可以转移,但是不能复制
bool outMsgQueue(int &num)
{
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::defer_lock);
//std::unique_lock<std::mutex> uniqueLock1(m_Mutex, std::defer_lock); // 此时uniqueLock1复制所有权是非法的
std::unique_lock<std::mutex> uniqueLock2( std::move(uniqueLock)); // 转移所有权是合法的
if (!m_RecvQueue.empty())
{
num = m_RecvQueue.front(); // 返回第一个元素,但是不检查元素是否存在;
m_RecvQueue.pop_front(); // 移除第一个元素但不返回
return true;
}
return false;
}
6、线程锁使用注意
线程锁,锁住的代码段要尽可能少,这样代码执行效率会高;锁定范围太高,代码执行效率低。要合理的控制互斥锁的锁定范围
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
using namespace std;
class dataShare
{
public:
std::unique_lock<std::mutex> rtn_unique_lock()
{
std::unique_lock<std::mutex> tempGuard(m_Mutex);
return tempGuard;
}
dataShare()
{
}
~dataShare()
{
}
// 存数据
void inMsgRecvQueue()
{
for (int i = 0; i < 1000; i++)
{
// 添加作用域
{
std::unique_lock<std::mutex> uniqueLock = rtn_unique_lock();
if (uniqueLock.owns_lock())
{
m_RecvQueue.push_back(i);
}
else
{
cout << "没有获得锁" << endl;
}
}
// doSomething
// ...
}
}
bool outMsgQueue(int &num)
{
std::unique_lock<std::mutex> uniqueLock(m_Mutex, std::defer_lock);
//std::unique_lock<std::mutex> uniqueLock1(m_Mutex, std::defer_lock); // 此时uniqueLock1复制所有权是非法的
std::unique_lock<std::mutex> uniqueLock2( std::move(uniqueLock)); // 转移所有权是合法的
if (!m_RecvQueue.empty())
{
num = m_RecvQueue.front(); // 返回第一个元素,但是不检查元素是否存在;
m_RecvQueue.pop_front(); // 移除第一个元素但不反回
return true;
}
return false;
}
// 取数据
void outMsgRecvQueue()
{
int num;
for (int i = 0; i < 1000; i++)
{
if (outMsgQueue(num))
{
cout << "delet one data" << num << endl;
}
else
{
cout << "outMsgRecvQueue no data" << endl;
}
}
cout << "end" << endl;
}
private:
std::list<int> m_RecvQueue;
//互斥量
std::mutex m_Mutex;
};
int main()
{
dataShare datas;
std::thread inMsgObj(&dataShare::inMsgRecvQueue, &datas);
std::thread outMsgObj(&dataShare::outMsgRecvQueue, &datas);
inMsgObj.join();
outMsgObj.join();
system("pause");
return 0;
}