C++多线程之unique_lock
逃离世界
一、为什么会有unique_lock?
因为mutex再管理方面有瑕疵,因此出现了一个互斥量封装器lock_guard来智能的管理mutex。但是lock_guard只是简单的管理,功能比较弱,有瑕疵。因此需要搞出来一个功能更强大的东西出来,而这个东西就是unique_lock。
本质上来说lock_guard和unique_lock都是为了更好地使用各种锁而诞生的,但是unique_lock更为灵活,功能更强大,可做的操作比较多。
unique_lock 有些时候也被称为“灵活锁”
二、std::lock_guard可能存在的问题
序号 | 问题描述 | 细节 |
---|---|---|
1 | 锁的粒度问题 | std::lock_guard的锁作用域与对象的作用域相同,可能导致锁的粒度过大,影响并发性能。 |
2 | 无法中途解锁 | 一旦std::lock_guard对象构造,锁将被自动获取,并在对象析构时自动释放,无法中途手动解锁 |
3 | 不支持条件变量 | std::lock_guard通常与互斥锁一起使用,但不支持与条件变量一起使用(如std::condition_variable)配合使用。 |
4 | 不支持递归锁 | 虽然可以与std::recursive_mutex一起使用,但std::lock_guard本身不提供对递归锁的特殊支持 |
5 | 无法指定锁的尝试获取 | std::lock_guard总是尝试获取锁,不支持非阻塞(尝试)获取锁 |
6 | 无法与超时机制结合 | std::guard不提供超时机制,无法设置获取锁的等待时间 |
三、什么是unique_lock?
unique_lock是一个更灵活的互斥量封装器,它提供了更多的控制选项,比如延迟锁定、尝试锁、递归锁定、定时锁定等。于std::lock_guard相比,std::unique_lock提供了更多的功能,但也需要更多的管理责任。
- 如何定义使用unique_lock?
·unique_lock的构造
std::mutex mtx;
std::unique_lock <std::mutex> lck1(mtx);//自动上锁
//自动锁定
std::unique_lock <std::mutex> lck2(mtx, std::defer_lock);//延迟锁定
std::unique_lock <std::mutex> lck3(mtx, std::adopt_lock);//接受已锁定的mutex,不允许再次锁定
std::unique_lock <std::mutex> lck4(mtx, std::try_to_lock);//尝试锁定
· std::defer_lock的具体含义是告诉std::unique_lock在构造时不要自动锁定互斥锁,而是延迟锁定操作
· std::unique_lock提供了一些成员函数,用于管理锁定状态
· lock()锁定关联的mutex
·unlock()解锁关联的mutex
· try_lock()尝试锁定mutex,如果锁定成功,返回true,否则返回false
· owns_lock()返回一个布尔值,指示unique_lock是否拥有mutex的所有权
- unique_lock的第二个参数
std::unique_lock的构造函数可以接受多个参数,其中第二个参数用于指定如何管理锁的行为。常见的第二个参数有以下几种:
· std::defer_lock
·std::try_to_lock
·std::adopt_lock
·超时相关参数(如std::chrono的时间段)
接下来我们依次用代码例子来看
- std::defer_lock
std::defer_lock表示延迟锁定。即在创建std::unique_lock对象时,不会立即对互斥锁进行锁定操作。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void example_defer_lock()
{
unique_lock<mutex> lock(mtx,std::defer_lock);//不会立即锁定
lock.lock();//在需要时显式锁定
for(int i=0;i<5;i++)
{
cout<<"Thread : "<<this_thread::get_id()<<" : "<<i<<endl;
}
cout<<"Locked with defer_lock"<<endl;
lock.unlock();//显式解锁
}
int main()
{
thread t1(example_defer_lock);
t1.join();
return 0;
}
这段代码主要展示了如何使用 unique_lock 结合 defer_lock 策略进行互斥锁的操作。通过使用
defer_lock,程序可以将锁的锁定操作延迟到需要的时候,这在某些情况下非常有用,例如在进行一些准备工作后再进行锁定,或者根据条件判断是否需要锁定。同时,使用
unique_lock 可以自动管理锁的生命周期,避免忘记解锁而导致的死锁问题,而显式的 lock 和 unlock
操作则提供了更灵活的控制方式,允许程序在锁定和解锁的时机上有更多的选择。在 example_defer_lock 函数中,先创建
unique_lock 但不锁定,之后根据需要进行锁定,在完成一些操作后解锁,保证资源的正确访问和释放,确保线程安全。
- std::try_to_lock
std::try_to_lock表示尝试锁定。在创建std::unique_lock对象时,会尝试锁定互斥锁。如果锁定失败,不会阻塞。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void example_try_lock()
{
unique_lock<mutex> lck(mtx, try_to_lock);//尝试锁定
if (lck.owns_lock())
{
cout <<this_thread::get_id() <<" I have the lock\n" << endl;
}
else
{
cout <<""<<this_thread::get_id()<< " I don't have the lock\n" << endl;
}
}
int main()
{
// 创建两个线程
thread t1(example_try_lock);//线程一锁定么互斥量
thread t2(example_try_lock);//线程二尝试锁定么互斥量,但是因为被线程一占用着,所以没有锁定成功
t1.join();
t2.join();
return 0;
}
这段代码主要展示了如何使用 unique_lock 的 try_to_lock
特性来尝试锁定互斥锁,并且根据锁定结果进行不同的处理。它创建了两个线程 t1 和 t2,这两个线程会尝试锁定同一个互斥锁
mtx。由于互斥锁的特性,只有一个线程能够成功锁定,另一个线程将无法锁定。通过 owns_lock
方法可以判断线程是否成功获取到锁,并输出相应的信息。这种方式可以避免线程阻塞,而是让线程在无法获取锁时继续执行其他任务或采取其他处理方式。同时,unique_lock
会在析构时自动解锁,避免了手动解锁的繁琐操作。
这种机制在多线程编程中非常有用,特别是当线程需要尝试获取资源,但不希望在获取不到时阻塞等待的情况,允许线程在不阻塞的情况下执行其他任务,提高程序的响应性和资源利用率。
3.std::adopt_lock
std::adopt_lock表示接管已经锁定的互斥锁。此参数假设互斥锁已经在其他地方被锁定,std::unique_lock对象会接管这个锁的所有权。使用了std::adopt_lock,这意味着互斥锁已经被锁定。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void example_adopt_lock()
{
mtx.lock();//先锁定互斥锁
unique_lock<mutex> lock(mtx, adopt_lock);//接管已锁定的锁
cout<<"Locked with adopt_lock"<<endl;
//mtx.unlock();
}
int main()
{
thread t1(example_adopt_lock);
t1.join();
return 0;
}
总体来说,这段代码的目的是展示如何在已经手动锁定互斥锁的情况下,使用 std::unique_lock 结合 adopt_lock 来管理该互斥锁,从而简化锁的管理并确保在适当的时候自动解锁。
总结
unique_lock的特点
- 灵活性:unique_lock提供了更多的控制选项,比如延迟锁定、尝试锁定、递归锁定、定时锁定等。这使得开发者可以根据具体的业务逻辑来精确控制锁的加锁和解锁时机。
- 可移动性:unique_lock是可移动的,可以拷贝、赋值或移动。这使得开发者可以在需要时轻松的将锁的所有权从一个对象转移到另一个对象。
- 手动控制:unique_lock支持手动解锁,而lock_guard不支持。这提供了更大的灵活性,但也需要开发者自行管理锁的加锁和解锁过程。
原文地址:https://blog.csdn.net/hujiahangdewa/article/details/145264895
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!