1、unique_lock取代lock_guard
unique_lock 是一个类模板。
unique_lock 比 lock_guard 灵活很多(多出来很多用法),效率差一点,内存占用多一些。
使用:unique_lock<mutex> myUniLock(myMutex);
2、unique_lock的第二个参数
2.1 std::adopt_lock:
std::adopt_lock:标记作用,表示这个互斥量已经被lock()(方便记忆:已经被lock()收养了,不需要再次lock() ),即不需要在构造函数中lock这个互斥量了。
前提:必须提前lock,否则会出错
lock_guard中也可以用这个参数
2.2 std::try_to_lock:
- 尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里;
- 使用try_to_lock的原因是防止其他的线程锁定mutex太长时间,导致本线程一直阻塞在lock 这个地方
前提:不能提前lock();
owns_lock()方法判断是否拿到锁,如拿到返回true
实例:
假设线程1 执行下例代码时,先锁定互斥量myMutex1,然后暂停了2s,才继续执行程序。线程1执行的代码如下
bool outMsgProc(int &num)
{
unique_lock<mutex> muGuard(myMutex1);
chrono::milliseconds time(2000); //线程暂停2s
this_thread::sleep_for(time);
if (!myList.empty())
{
num = myList.front();
myList.pop_front();
return true;
}
return false;
}
在此期间,如果线程2也尝试锁定myMutex1,就只能阻塞,直到线程1释放锁后,才能继续执行。线程2执行的代码如下:
void InMsg()
{
for (int i = 0; i < 10000; i++)
{
cout << "插入元素: " << i << endl;
unique_lock<mutex> muGuard(myMutex1);
myList.push_back(i);
}
}
显然,这样的程序效率不高。线程2花费了太多的时间等待。
我们考虑使用try_to_lock,线程尝试获取锁,如果没有锁定成功,它不会阻塞在那里,可以去执行其他代码。改进后的代码如下:
void InMsg()
{
for (int i = 0; i < 10000; i++)
{
cout << "插入元素: " << i << endl;
unique_lock<mutex> muGuard(myMutex1, try_to_lock);
if (muGuard.owns_lock())// 判断是否拿到锁
{
myList.push_back(i);
}
else
{
//没有拿到锁时,执行的代码
}
}
}
2.3 std::defer_lock:
- 如果没有第二个参数就对mutex进行加锁,加上defer_lock是始化了一个没有加锁的mutex
- 不给它加锁的目的是以后可以调用unique_lock的一些方法
- 前提:不能提前lock
3、unique_lock的成员函数(前三个与std::defer_lock联合使用)
3.1 lock():加锁。
unique_lock<mutex> muGuard(myMutex, defer_lock);
muGuard.lock();//手动加锁
不用自己unlock();
3.2 unlock():解锁。
unique_lock<mutex> muGuard(myMutex, defer_lock);
muGuard.lock();
//处理一些共享代码
muGuard.unlock();
//临时处理一些非共享代码
muGuard.lock();
//处理一些共享代码,处理完之后,自动解锁
3.3 try_lock():尝试给互斥量加锁
try_to_lock是unique_lock的第二个参数,try_lock()是unique_lock()的成员变量。
如果拿不到锁,返回false,否则返回true(然后上锁)。
unique_lock<mutex>muGuard(myMutex, defer_lock);
//mutex1.lock();//自动解锁
if (muGuard.try_lock() == true) {
cout << "插入数据: " << num << endl;
test_list.push_back(num);
}
else {
cout << "in_list()执行,但没有拿到锁" << num << endl;
}
3.4 release():
release():就是解除绑定,返回它所管理的mutex对象的指针,并释放所有权。
-
unique_lock<mutex>muGuard(myMutex1);
相当于把myMutex(mutex对象)和 muGuard绑定在了一起 -
mutex* ptx =muGuard.release();
也就是说 muGuard和mutex不在有联系,后续myMutex所有权由ptx接管,如果原来mutex对象处理加锁状态,就需要ptx在以后进行解锁了。
for (int num = 0; num < 10000; num++) {
unique_lock<mutex> muGuard(myMutex);
mutex* ptx = muGuard.release();//解除myMutex1(mutex对象)和 muGuard绑定
//操作事务
test_list.push_back(num);
ptx->unlock();//myMutex1所有权由ptx接管,由ptx进行解锁
}
lock的代码段越少,执行越快,整个程序的运行效率越高。
- 锁住的代码少,叫做粒度细,执行效率高;
- 锁住的代码多,叫做粒度粗,执行效率低;
-只锁定共享的数据
4.unique_lock所有权的传递
unique_lock<mutex> muGuard1(myMutex);
把myMutex1和muGuard绑定在了一起,也就是muGuard拥有myMutex1的所有权文章来源:https://www.toymoban.com/news/detail-655872.html
4.1 使用move转移
unique_lock<mutex> muGuard2(std::move(muGuard1));
之前muGuard1拥有myMutex的所有权,muGuard1可以把自己对myMutex的所有权转移,但是不能复制。现在muGuard2拥有myMutex的所有权。文章来源地址https://www.toymoban.com/news/detail-655872.html
4.2. 在函数中return一个临时变量,也可以实现转移
unique_lock<mutex> aFunction()
{
unique_lock<mutex> tmpguard(myMutex);
//移动构造函数那里讲从函数返回一个局部的unique_lock对象是可以的
//返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
return tmpguard;
}
// 然后就可以在外层调用,在muGuard2具有对myMutex的所有权
std::unique_lock<std::mutex> muGuard2 = aFunction();
到了这里,关于C++11并发与多线程笔记(6) unique_lock(类模板)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!