C++多线程学习07 unique_lock与scoped_lock

一、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

猜你喜欢

转载自blog.csdn.net/qq_42567607/article/details/125610157