概述
一、unique_lock唯一锁
lock_guard 最大的缺点是简单,没有给程序员提供足够的灵活度,因此,C++11 标准中定义了另外一个与 Mutex RAII 相关类 unique_lock,该类与 lock_guard 类相似,也很方便线程对互斥量上锁,但它提供了更好的上锁和解锁控制。
顾名思义,unique_lock 对象以独占所有权的方式( unique owership)管理 mutex 对象的上锁和解锁操作,所谓独占所有权,就是没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权。
在构造(或移动(move)赋值)时,unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的 unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作。
std::unique_lock 对象也能保证在其自身析构时它所管理的 Mutex 对象能够被正确地解锁(即使没有显式地调用 unlock 函数)。因此,和 lock_guard 一样,这也是一种简单而又安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
值得注意的是,unique_lock 对象同样也不负责管理 Mutex 对象的生命周期,unique_lock 对象只是简化了 Mutex 对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个 unique_lock 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 unique_lock 的生命周期结束之后,它所管理的锁对象会被解锁,这一点和 lock_guard 类似,但 unique_lock 给程序员提供了更多的自由,我会在下面的内容中给大家介绍 unique_lock 的用法。
参考:https://zhangzc.blog.csdn.net/article/details/96852295?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7-96852295-blog-51813140.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7-96852295-blog-51813140.pc_relevant_aa2&utm_relevant_index=9
先来看看unique_lock的定义:
template <class _Mutex>
class unique_lock { // whizzy class with destructor that unlocks mutex
public:
using mutex_type = _Mutex;
// CONSTRUCT, ASSIGN, AND DESTROY
unique_lock() noexcept : _Pmtx(nullptr), _Owns(false) {}
explicit unique_lock(_Mutex& _Mtx) : _Pmtx(_STD addressof(_Mtx)), _Owns(false) { // construct and lock
_Pmtx->lock();
_Owns = true;
}
unique_lock(_Mutex& _Mtx, adopt_lock_t)
: _Pmtx(_STD addressof(_Mtx)), _Owns(true) { // construct and assume already locked
}
unique_lock(_Mutex& _Mtx, defer_lock_t) noexcept
: _Pmtx(_STD addressof(_Mtx)), _Owns(false) { // construct but don't lock
}
unique_lock(_Mutex& _Mtx, try_to_lock_t)
: _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock()) { // construct and try to lock
}
补充一个关键字noexcept
noexcept形如其名地,表示其修饰的函数不会抛出异常。不过与throw()动态异常声明不同的是,在C++11中如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()函数来终止程序的运行,这比基于异常机制的throw()在效率上会高一些。这是因为异常机制会带来一些额外开销,比如函数抛出异常,会导致函数栈被依次地展开(unwind),并依帧调用在本帧中已构造的自动变量的析构函数等。
unique_lock与lock_guard()一样是一个模板类,根据传进来的互斥量类型的不同生成不同种类的对象,其构造函数有以下几种:
01无参的
02只有互斥量的:此时与lock_guard一致,新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞。
03除了互斥量还有adopt_lock的:已经拥有锁,不加锁而是收养已拥有的锁,出栈区会释放这个锁,_Owns(true)会将拥有设为true,导致在析构时会释放锁
~unique_lock() noexcept {
if (_Owns) {
_Pmtx->unlock();
}
}
04除了互斥量还有 defer_lock的 :新创建的 unique_lock 对象管理 Mutex 对象 m,但是在初始化的时候并不锁住 Mutex 对象,可以延后通过unique_lock来进行加锁。 m 应该是一个没有当前线程锁住的 Mutex 对象。_Owns(False)会将拥有设为false,导致在析构时不会解锁
05除了互斥量还有try_to_lock的:新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,并不会阻塞当前线程。_Owns(_Pmtx->try_lock()),让mutex对象尝试去上锁,上锁成功的话 将_Owns设为true,失败的话设为false
class XMutex
{
public:
XMutex(mutex& mux):mux_(mux)
{
cout << "Lock" << endl;
mux.lock();
}
~XMutex()
{
cout << "Unlock" << endl;
mux_.unlock();
}
private:
mutex& mux_;
};
int main(int argc, char* argv[])
{
{
static mutex mux;
{
unique_lock<mutex> lock(mux);//与lock_guard效果一样
lock.unlock();//lock_guard不能临时释放
//临时释放锁
lock.lock();
}
{
//已经拥有锁 不锁定,退出栈区解锁
mux.lock();
unique_lock<mutex> lock(mux, adopt_lock);
}
{
//延后加锁 不拥有 退出栈区不解锁
unique_lock<mutex> lock(mux, defer_lock);
//加锁 退出栈区解锁
lock.lock();
}
{
//mux.lock();
//尝试加锁 不阻塞 失败不拥有锁
unique_lock<mutex> lock(mux, try_to_lock);
if (lock.owns_lock())
{
cout << "owns_lock" << endl;
}
else
{
cout << "not owns_lock" << endl;
}
}
}
getchar();
return 0;
}
unique_lock lock(mux, try_to_lock);前mux已解锁地干干净净,所以try_to_lock上锁成功
二、scoped_lock范围锁
scoped_lock C++17 用于多个互斥体的免死锁 RAII 封装器 类似lock
最后
以上就是怕孤单翅膀为你收集整理的C++多线程学习07 unique_lock与scoped_lock的全部内容,希望文章能够帮你解决C++多线程学习07 unique_lock与scoped_lock所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复