1、unique_lock介绍
-
unique_lock取代lock_guard,unique_lock更像lock_guard的升级版,也是mutex的管家!
-
unique_lock是个类模板,工作中一般使用lock_guard(推荐使用)
- lock_guard取代了mutex的lock()和unlock()
-
unique_lock比lock_guard灵活很多,效率差一点,内存占用多一点
- lock_guard额外参数只能传入std::adopt_lock
- unique_lock额外参数可以传入std::adopt_lock、std::try_to_lock、std::defer_lock...
-
额外参数的解释
- std::adopt_lock:标记作用,通知lock_guard或unique_lock不需要再构造函数中lock(),标记的效果就是"假设调用方线程已经拥有了互斥的所有权(已经lock()成功了)"
- std::try_to_lock:尝试获取锁,如果获取失败将不会阻塞在锁头上,会继续向下执行
- std::defer_lock:初始化一个没有加锁的mutex
源码参数分析
cpp
template<typename _Mutex>
class unique_lock {
public:
typedef _Mutex mutex_type;
unique_lock() noexcept
: _M_device(0), _M_owns(false) {}
explicit unique_lock(mutex_type &__m)
: _M_device(std::__addressof(__m)), _M_owns(false) {
lock();
_M_owns = true;
}
unique_lock(mutex_type &__m, defer_lock_t) noexcept
: _M_device(std::__addressof(__m)), _M_owns(false) {}
unique_lock(mutex_type &__m, try_to_lock_t)
: _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock()) {}
unique_lock(mutex_type &__m, adopt_lock_t) noexcept
: _M_device(std::__addressof(__m)), _M_owns(true) {
// XXX calling thread owns mutex
}
template<typename _Clock, typename _Duration>
unique_lock(mutex_type &__m,
const chrono::time_point <_Clock, _Duration> &__atime)
: _M_device(std::__addressof(__m)),
_M_owns(_M_device->try_lock_until(__atime)) {}
template<typename _Rep, typename _Period>
unique_lock(mutex_type &__m,
const chrono::duration <_Rep, _Period> &__rtime)
: _M_device(std::__addressof(__m)),
_M_owns(_M_device->try_lock_for(__rtime)) {}
~unique_lock() {
if (_M_owns)
unlock();
}
};
- unique_lock()构造:获取一把空锁,所有权也拿不到为false。
- unique_lock(mutex_type &__m)构造:传入一个互斥量作为构造参数,然后加锁,持有权为true
- unique_lock(mutex_type &__m, adopt_lock_t)构造:传入mutex和std::adopt_lock,不在加锁,但是持有权默认为true
- unique_lock(mutex_type &__m, try_to_lock_t)构造:传入mutex和std::try_to_lock,尝试加锁,持有权为尝试加锁的结果
- unique_lock(mutex_type &__m, defer_lock_t) 构造:传入mutex和std::defer_lock,传入一把锁但不加锁,持有权为false
- 另外下面几个带chrono的构造表示睡眠的时间构造。
- 析构函数很特别:析构函数里写的如果持有锁那么就释放锁,如果不持有锁那么就不释放锁。因此如果我们提前释放锁,那么它不释放
看完上面的构造源码和析构源码,那么下面使用这些参数和构造方法也是一件非常简单的事情了!
2、std::adopt_lock
cpp
std::mutex mutex_lock;
mutex_lock.lock();
std::unique_lock<std::mutex>(mutex_lock, std::adopt_lock);
std::adopt_lock的含义是高速unique_lock不需要你再去加锁,因此需要手动去加锁,否则没有效果,如上面构造源码。
3、std::try_to_lock的使用
- 尝试用mutex的lock()去锁定这个mutex,如果没有锁成功也会立即返回并不会阻塞在锁头上,前提是不能先lock()
- out方法中对线程进行抱着锁睡2秒
- 如果当执行poll_msg函数的线程1先拿到锁,那么线程2可能在队列中一个元素也不会放进去。因为一旦获取锁失败,循环将继续向下走。
- 如果线程2先拿到锁,在一个时间片内(具体多久看操作系统的调度)会尽可能的多次获取到锁尝试放入元素。
cpp
void inMsgRecvQueue(){
for(int i = 0;i < n;i++){
// 尝试获取锁
std::unique_lock<std::mutex> uniqueLock(mutex_lock, std::try_to_lock);
if(uniqueLock.owns_lock()){
q.push(i);
std::cout << "inMsgRecvQueue()执行, 插入一个元素i = " << i << std::endl;
}
}
}
int poll_msg(){
int msg = -1;
std::unique_lock<std::mutex> uniqueLock(mutex_lock, std::try_to_lock);
std::chrono::milliseconds duration(2000);
std::this_thread::sleep_for(duration);
std::cout << "queue size = " << q.size() << std::endl;
if(q.size()) {
msg = q.front();
q.pop();
}
return msg;
}
3、std::defer_lock的使用
-
这个参数的构造函数很有趣,将unique_lock与mutex绑定在一起,但是没有加锁也没有持有权。
-
但是我们可以通过unique_lock提供的函数去加锁、解锁、尝试锁...
cpp
std::unique_lock<std::mutex> uniqueLock(mutex_lock, std::defer_lock);
uniqueLock.lock(); // 加锁
uniqueLock.owns_lock(); // 是否持有锁:有返回true,否则返回false
uniqueLock.unlock(); // 解锁
std::mutex *ptr = uniqueLock.release(); // 将unique_lock与mutex解除绑定,并且返回指向mutex的指针
uniqueLock.try_lock(); // 尝试获取锁,成功true,失败false
uniqueLock.mutex(); // 返回unique_lock绑定的mutex对象
-
其实unique_lock传入defer_lock只是进行了一层封装,上面这些加锁解锁的函数mutex也有。
-
提供这些函数可以很灵活的控制临界区的大小,也就是程序的粒度大小,粒度越细程序越快。
4、unique_lock所有权的传递
其实unique_lock中还有下面几个拷贝构造和赋值=运算符的重载
cpp
unique_lock(const unique_lock&) = delete;
unique_lock& operator=(const unique_lock&) = delete;
unique_lock(unique_lock&& __u) noexcept : _M_device(__u._M_device), _M_owns(__u._M_owns){
__u._M_device = 0;
__u._M_owns = false;
}
unique_lock& operator=(unique_lock&& __u) noexcept{
if(_M_owns)
unlock();
unique_lock(std::move(__u)).swap(*this);
__u._M_device = 0;
__u._M_owns = false;
return *this
-
unique_lock(unique_lock&& __u) :这是一个锁的移动构造,如果传入一把锁mutex进来,然后交给当前unique_lock持有,原先的unique_lock不在持有这把锁
cpp// 1 std::unique_lock<std::mutex> uniqueLock1(mutex_lock); std::unique_lock<std::mutex> uniqueLock2(std::move(uniqueLock1)); // 2 std::unique_lock<std::mutex> return_unique_lock(){ std::unique_lock<std::mutex> uniqueLock(mutex_lock); return uniqueLock; } std::unique_lock<std::mutex> uniqueLock = return_unique_lock();
最后uniqueLock2持有mutex_lock,uniqueLock1不再持有
-
unique_lock& operator=(unique_lock&& __u) :这是一个返回类型为引用的赋值函数重载,如果有可能将原先持有的解锁,然后拿到传入进来的锁,并且将当前的锁返回,返回的是一个引用,因此还可以继续链式编程,但是这个方法已经废弃...
cppstd::unique_lock<std::mutex> uniqueLock1(mutex_lock); std::unique_lock<std::mutex> uniqueLock2(mutex_lock); // uniqueLock2 = uniqueLock1; // uniqueLock1.operator=(uniqueLock2);