六、unique_lock详解
本节详细记录std::unique_lock
类模板。
std::unique_lock
是一个类模板,工作中一般使用lock_guard
。lock_guard
取代了mutex
的lock()
与unlock()
,unique_lock
也是实现自动加锁的,相比于lock_guard
它更加灵活,但是也更消耗资源。默认构造的情况下,unique_lock
可以完全取代lock_guard
。
1、unique_lock的第二参数
1.1、adopt_lock
std::adopt_lock
:表示这个互斥量已经被lock,(必须保证互斥量提前被lock了,才能使用此参数,否则会报异常)
1 | my_mutex2_.lock(); // 一定要先lock()才能使用std::adopt_lock参数 |
1.2、try_to_lock
std::try_to_lock
:使用try_to_lock
的前提是不能自己先去lock。此标志表示unique_lock
会尝试用mutex
的lock()去锁这个mutex
,但如果没有锁定成功,会立即返回,并不会阻塞在哪里。下面程序展示一个例子:
1 | void inMsgRecvQueue() // 线程B入口函数 |
上述两个函数是两个线程的入口函数,在其中一个线程A中,A拿到锁后线程sleep了20s,若使用传统的mutex
,线程B因为拿不到锁也会阻塞至少20s。使用了std::try_to_lock
参数后,线程B会尝试拿锁,在未拿到锁头的情况下线程B不会阻塞,我们可以去处理一些非共享的数据。
1.3、defer_lock
std::defer_lock
:使用的前提是不能自己先lock,否则会报异常。它的意思是并没有给mutex
加锁,初始化了一个没有加锁的mutex
。
1 | void inMsgRecvQueue() // 线程入口函数 |
2、unique_lock的成员函数
2.1、lock()
在如上程序中的演示。
2.2、unlock()
在如上程序中的演示。lock锁住的代码越少,执行越快,程序的效率越高,有人也把锁头锁住的代码多少叫做粒度。
- 锁住的代码少,粒度细,执行效率高;
- 锁住的代码多,粒度粗,执行效率低;
2.3、try_lock()
尝试给互斥量加锁,如果拿不到锁,则返回false,否则返回true,这个函数不阻塞。(和第二个参数中的std::try_to_lock
相似)。用法如下:
1 | std::unique_lock<std::mutex> auto_mutex_1(my_mutex1_,std::defer_lock); |
2.4、release()
返回它所管理的mutex
对象指针,并释放所有权,等同于unique_lock
和mutex
不在有联系。如果原来mutex
对象处于加锁状态,有责任接管过来后负责解锁。用法:
1 | std::unique_lock<std::mutex> auto_mutex_1(my_mutex1_); // 默认情况下构造时加锁 |
3、unique_lock所有权的传递
1 | std::unique_lock<std::mutex> auto_mutex_1(my_mutex1_); |
此时auto_mutex_1拥有my_mutex1_的所有权,auto_mutex_1可以把自己对mutex
(my_mutex_1)的所有权转移给其他的unique_lock
对象;unique_lock对mutex的所有权可以转移,但是不可以复制!
通过移动语义转移所有权:
1 | std::unique_lock<std::mutex> auto_mutex_1(my_mutex1_); |
通过成员函数返回临时对象:
1 | std::unique_lock<std::mutex> rtn_unique_lock() |