std::unique_lock与std::lock_guard
一、介绍
二、区别
c++11中有两个区域锁:lock_guard和unique_lock。
- 区域锁lock_guard使用起来比较简单,除了构造函数外没有其他member function,在整个区域都有效。
- 区域锁unique_lock除了lock_guard的功能外,提供了更多的member_function,相对来说更灵活一些;但是在unique_lock占用地内存更多,效率也有所下降。
三、unique_lock和lock_guard
3.1 声明
unique_lock相交于lock_guard更加灵活,可以手动进行解锁,但是在日常编程中,还是以lock_guard为主。但是标准库也提供了第二参数的构造函数。例如:
1 explicit lock_guard (mutex_type& m);
2 lock_guard (mutex_type& m, adopt_lock_t tag);
3
4 explicit unique_lock (mutex_type& m);
5 unique_lock (mutex_type& m, try_to_lock_t tag);
6 unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
7 unique_lock (mutex_type& m, adopt_lock_t tag);
8 ... ...
3.1 unique_lock部分成员函数
unique_lock的最有用的一组函数为:
locks the associated mutex (public member function) |
|
tries to lock the associated mutex, returns if the mutex is not available (public member function) |
|
attempts to lock the associated TimedLockable mutex, returns if the mutex has been unavailable for the specified time duration (public member function) |
|
tries to lock the associated TimedLockable mutex, returns if the mutex has been unavailable until specified time point has been reached (public member function) |
|
unlocks the associated mutex |
- 通过上面的函数,可以通过lock/unlock可以比较灵活的控制锁的范围,减小锁的粒度。
- 通过try_lock_for/try_lock_until则可以控制加锁的等待时间,此时这种锁为乐观锁。
3.2 unique_lock取代lock_guard
unique_lock是个类模板,工作中,一般lock_guard(推荐使用);
lock_guard取代了mutex的lock()和unlock()。
unique_lock比lock_guard灵活很多灵活很多;效率上差一点,内存占用多一点。
使用时std::lock_guard<std::mutex> lk(mtx);直接替换成std::unique_lock<std::mutex> lk(mtx);
3.3 lock_guard和unique_lock第二参数的作用
3.3.1 std::adopt_lock
std::adopt_lock标记的效果就是假设调用一方已经拥有了互斥量的所有权(已经lock成功了);通知lock_guard不需要再构造函数中lock这个互斥量了。
1 std::lock_guard<std::mutex> sbguard(my_mutex,std::adopt_lock);
对于lock_guard第二参数类型只有一种,锁管理器构造的时候不会自动对可锁对象上锁;由可锁对象自己加锁;等锁管理器析构的时候自动解锁。
两种使用方法
1 {
2 std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);
3 g_mtx.lock();
4 临界区或临界资源
5 }
或者
1 {
2 g_mtx.lock();
3 std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);
4 临界区或临界资源
5 }
如果我们指定了第二参数,但是没有lock,锁管理器析构的时候解锁了无拥有权的可锁对象,导致异常。
为什么需要使用第二参数构造函数;直接在锁管理器构造的时候自动加锁不好吗?其实官方提供第二参数构造函数是有其他作用的。比如多锁场景下,我们会调用std::lock避免死锁的出现,但是这个方法要求锁管理器不能拥有可锁对象,由std::lock方法执行锁操作。如果没有提供第二参数构造函数,那么我们就无法使用该方法了。
std::adopt_lock和lock_guard第二参数作用类似。锁管理器假设拥有可锁对象,在锁管理器析构的时候自动解锁。注意:使用该参数类型构造的锁管理器必须只能通过可锁对象进行lock,不可通过锁管理器进行lock,误用会导致程序异常。
3.3.2 std::defer_lock
用std::defer_lock的前提是,你不能自己先lock,否则会报异常,std::defer_lock的意思就是并没有给mutex加锁:初始化了一个没有加锁的mutex。
1 {
2 std::unique_lock<std::mutex> lock(g_mtx, std::defer_lock);
3 lock.lock(); // 不能用g_mtx.lock(),第二次锁的时候会崩溃
4 临界区或临界资源
5 }
std::defer_lock参数作用:锁管理器在构造的时候不主动lock且不拥有可锁对象;如果后续执行lock,锁管理器析构的时候自动解锁。注意:该类型构造的锁管理器只能通过锁管理器执行lock且拥有可锁对象。如果直接调用可锁对象进行锁操作后,会导致程序异常;详情可参考源码。
3.3.2 std::try_to_lock
1 {
2 std::unique_lock<std::mutex> lock(g_mtx, std::try_to_lock);
3 if (lock.owns_lock()) {
4 临界区或临界资源
5 }
6 }
std::try_to_lock参数作用:锁管理器在构造的时候尝试lock;如果lock上,锁管理器就拥有可锁对象(持有锁),析构的时候自动执行解锁;否则就不持可锁对象,析构的时候也就不会解锁;它的好处是当某个线程尝试获取该该锁,但是该锁已经已被其他线程持有,那么不会出现线程被阻塞挂起。
四、参考文章
https://www.cnblogs.com/smartNeo/p/14624156.html
https://blog.csdn.net/u012507022/article/details/85909567?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EsearchFromBaidu%7Edefault-1.pc_relevant_baidujshouduan&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EsearchFromBaidu%7Edefault-1.pc_relevant_baidujshouduan
本文来自博客园,作者:Mr-xxx,转载请注明原文链接:https://www.cnblogs.com/MrLiuZF/p/15081201.html