<mutex>注释 9:类 timed_mutex 及 recursive_timed_mutex 的源码与举例,unique_lock <T> 不管理递归锁,

(42)类 timed_mutex 的源码与举例,此类是个组合类,不是基础类。该类的功能是依据基础的 mutex 与 条件变量组合起来的。 mutex 与 condition_variable 是完全的新的类,具有崭新的功能。但 condition_variable 的限时等待,对待时间的处理,不准确。
本 timed_mutex 是完全遵守时间限制的类,限时加锁,如果加不上,线程还是会继续执行余下的代码,而不是继续进入睡眠

在这里插入图片描述

++ 其源码如下

struct _UInt_is_zero // 本类是可调用对象,实现了括号运算符函数。
{
    const unsigned int& _UInt; // 数据成员是左值引用,看来是保存了一个整型数据

    bool operator()() const { return _UInt == 0; } // 括号运算符函数,数据为 0 则返回 true 
};

// 同时具有 mutex 和 condition_variable 的另一个类是 <future> 中的 保存函数返回值的 _Associated_state<_Ty>
// class for timed mutual exclusion 定时互斥的类 , 本类没有继承 _Mutex_base
class timed_mutex  // 同时结合了 mutex 与 condition_variable
{ 	// _My_locked 的含义是线程对锁加了几次锁。但本 timed_mutex 非递归锁,统计加锁次数无意义,故赋值为全 F。
private:                             // _My_locked 非 0 表已有线程抢占了临界资源 ;
    unsigned int        _My_locked ; // 此值为 0 表本线程可以占有临界资源,并修改本值为 非0 。
    mutex               _My_mutex  ; // 此处的 mutex 保护的是对数据成员 _My_locked 的访问的原子性
    condition_variable  _My_cond   ; // 为了实现线程同步, mutex 与条件变量总是同时使用、同时出现。
    
public:
    timed_mutex() noexcept : _My_locked(0) {}  // 默认的构造函数。没有显式定义的析构函数

    timed_mutex(const timed_mutex&) = delete;  // 禁止了 copy 构造函数
    timed_mutex& operator=(const timed_mutex&) = delete; // 禁止了 copy 赋值运算符

    void lock() // lock the mutex,但本函执行完毕时就释放了 mutex 。本类中的 mutex 成员大部分时间是空闲的。
    {           // 本成员函数只是独占地修改了数据成员 _My_locked 的值为 非零。
        unique_lock<mutex> _Lock(_My_mutex); // 此行代码的执行,隐含了对 mutex 的加锁成功。

        // 意思是即使拿到了锁,但若 _My_locked != 0 ,也要释放锁并等待唤醒。
        while ( _My_locked != 0 )     _My_cond.wait(_Lock); 
                          
        _My_locked = UINT_MAX; // UINT_MAX = 0xffffffff ,本线程拿到临界资源后就修改 _My_locked 为 非0
    }

    void unlock()  // unlock the mutex 本类的释放锁后,还有新的功能就是执行条件变量的唤醒。
    { 
        {   // template <class _Mutex>
            // class lock_guard { _Mutex& _MyMutex; ~lock_guard() { _MyMutex.unlock(); } };
            lock_guard<mutex> _Lock(_My_mutex);  // The lock here is necessary
            _My_locked = 0;  // 强制加锁,然后把  _My_locked 修改为 0 , 再释放锁
        }

        _My_cond.notify_one(); // 只唤醒一个等待的线程。
    }

    bool try_lock() noexcept // try to lock the mutex ,
    {   // 本函与 mutex.try_lock(...) 的实现不同,但语义还是相同的。 true 表加锁成功。
        lock_guard<mutex> _Lock(_My_mutex);   // 此行语句的执行,表示加锁成功
        if (_My_locked != 0)    return false; // 非 0,返 F,表别的线程在使用临界资源
        else {
            _My_locked = UINT_MAX ;
            return true;
        }
    }

    //******************************

    // 本函的语义是 : 在限时内尝试对临界资源加锁,加锁成功则返回 true ;始终加不上则返回 false
    template <class _Time>                   // try to lock the mutex with timeout
    bool _Try_lock_until( _Time  _Abs_time ) // 本函是最基本的成员函数,被别的成员函数调用
    {
        unique_lock<mutex> _Lock(_My_mutex); // 加锁成功
        
        // 条件变量 condition_variable 的限时等待的语义是:若未超时下的断言为假,
        // 则继续睡眠等待直到断言为真; 超时后的条件唤醒则直接返回断言结果,断言为假也不再睡眠。
        if (  ! _My_cond.wait_until(_Lock, _Abs_time, _UInt_is_zero{ _My_locked } )  )
            return false; 
            
        _My_locked = UINT_MAX;
        return true;
    }

    // try to lock the mutex with timeout  调用上面的更底层的函数,语义完全一样
    bool try_lock_until(const xtime* _Abs_time) { return _Try_lock_until(_Abs_time); }

    template <class _Clock, class _Duration>  // try to lock the mutex with timeout
    bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time)
    {
#if _HAS_CXX20
        static_assert(chrono::is_clock_v<_Clock>, "Clock type required");
#endif // _HAS_CXX20
        return _Try_lock_until(_Abs_time);    // 调用了上面的成员函数
    }

    template <class _Rep, class _Period>   // try to lock for duration
    bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time) // 调用上面的成员函数
    { 
        return try_lock_until( _To_absolute_time(_Rel_time) );  
    }
    // 函 _To_absolute_time 的返回值类型是 time_point <  steady_clock , duration<long_long,nano>  >
    // auto _To_absolute_time (const chrono::duration<_Rep, _Period>& _Rel_time ) 
};

(43) 类 recursive_timed_mutex 的源码与举例,该类允许持有锁的线程,再次对锁加锁,而不报错:

// enum { _Mtx_plain=0x01 , _Mtx_try=0x02 , _Mtx_timed=0x04 , _Mtx_recursive=0x100 }; // mutex types
// class _Mutex_base { _Mutex_base(int _Flags = 0) { _Mtx_init_in_situ(_Mymtx(), _Flags | _Mtx_try); }  };
// class recursive_mutex : _Mutex_base {  recursive_mutex() : _Mutex_base(_Mtx_recursive) {} };
class recursive_timed_mutex          // class for recursive timed mutual exclusion
{ 
private:                             // _My_locked 非 0 表已有线程抢占了临界资源 ;
    unsigned int        _My_locked ; // 此值为 0 表本线程可以占有临界资源,并修改本值为 非0 。
    mutex               _My_mutex  ; // 此处的 mutex 保护的是对数据成员 _My_locked 的访问的原子性
    condition_variable  _My_cond   ; // 为了实现线程同步, mutex 与条件变量总是同时使用、同时出现。
    thread::id          _My_owner  ; // 记录本锁已被哪个线程拥有

public:
    // 默认的构造函数。没有显式定义的析构函数。_My_locked 的含义是本线程加了几次锁,故初始化为 0 。
    recursive_timed_mutex() noexcept : _My_locked(0) {} 

    recursive_timed_mutex(const recursive_timed_mutex&) = delete;  // 禁止 copy 构造函数
    recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; // 禁止 copy 赋值运算符函数

    void lock()  // lock the mutex 。从本函数可以看出:递归锁,加了几次锁,也要解锁几次。!!
    { 
        const thread::id _Tid = this_thread::get_id(); // 获取运行此函数的线程的 id 

        unique_lock<mutex> _Lock(_My_mutex); // 加锁

        if (_Tid == _My_owner) // 若是已经持有锁的线程对锁的重复加锁
        {
            if (_My_locked < UINT_MAX)   // 可见,允许持有锁的线程再次加锁
                ++_My_locked;  // 更新加锁的次数
            else               // 加锁次数太多,报错。但这个数据非常大, u_int 的最大值。
                _Throw_system_error(errc::device_or_resource_busy);
        } 
        else  // 未持有锁的线程也想加锁,只能睡眠等待,这符合 锁 的语义,lock()函数的定义
        {
            while ( _My_locked != 0 )   _My_cond.wait(_Lock); // wait(..) 睡眠前,本线程会释放锁的 

            _My_locked = 1;   // 出了上面的 while 与 wait ,此时当前线程已经获得锁了
            _My_owner  = _Tid;
        }
    }

    // 可见本模板的 unlock() 函数还兼有 condition_variable.notify() 的功能
    void unlock()  // unlock the mutex 没把本线程加锁的次数解锁完,就不必唤醒别的等待锁的线程
    {
        bool _Do_notify = false;  // 只有在本线程加锁的次数,全部清零,才可以解锁,否则不解锁

        {
            lock_guard<mutex> _Lock(_My_mutex); // 缩小了锁保护的代码段

            --_My_locked;  // 加锁的次数减一

            if (_My_locked == 0) {
                _Do_notify = true;
                _My_owner = thread::id();  // 把记录线程 id 的字段清零
            }
        }

        if ( _Do_notify )  _My_cond.notify_one();   // 唤醒等待的线程
    }

    bool try_lock() noexcept  // try to lock the mutex
    { 
        const thread::id _Tid = this_thread::get_id();   // 获取运行此函数的当前线程的 id 

        lock_guard<mutex> _Lock(_My_mutex);   // 此锁在本函数退出时,解开。

        if (_Tid == _My_owner)      // 说明获得 锁 的线程,也可以多次调用 try_lock()
        {
            if (_My_locked < UINT_MAX) 
                ++_My_locked;       // 叠加加锁的次数,跟 lock(..) 的功能一样
            else 
                return false;
        }
        else 
        {
            if (_My_locked != 0) // 若别的线程得不到锁, try_lock 不会 wait 等待,直接返回 false
                return false;
            else                 // 说明锁空闲了。没有任何线程占有锁。所以本线程可以占有锁。
            {
                _My_locked = 1;
                _My_owner  = _Tid;
            }
        }
        return true;
    }

    //********************以下函数与时间有关了

    template <class _Time>  // try to lock the mutex with timeout
    bool _Try_lock_until(_Time _Abs_time) // 本函数是最基础的函数,被别的成员函数调用
    {
        const thread::id _Tid = this_thread::get_id();  // 获取运行此函数的线程的 id 

        unique_lock<mutex> _Lock(_My_mutex);

        if (_Tid == _My_owner)  // 说明本线程在获得锁以后,又请求加锁
        {
            if (_My_locked < UINT_MAX)    // 这里说明加上锁了,不用等待
                ++_My_locked;
            else
                return false;
        }
        else   // 此时可能锁空闲,成员 _My_owner = 0 ; 也可能是锁被别的线程占有
        {   // condition_variable.wait_until(..) 有两个版本,支持不同形式的时间点
            if (!_My_cond.wait_until(_Lock, _Abs_time, _UInt_is_zero{ _My_locked }))
                return false;
    // struct _UInt_is_zero { u_int& _UInt;  bool operator()() { return _UInt == 0; }  };

            _My_locked = 1;
            _My_owner = _Tid;  // 数据成员 _My_owner 更改为当前线程的 id 
        }

        return true;
    }

    // try to lock the mutex with timeout , 可见,这俩成员函数的功能完全一样,只是函数名不同
    bool try_lock_until(const xtime* _Abs_time) {  return _Try_lock_until(_Abs_time);  }

    template <class _Clock, class _Duration>   // try to lock the mutex with timeout
    bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) // 重载成员函数
    {
#if _HAS_CXX20
        static_assert(chrono::is_clock_v<_Clock>, "Clock type required");
#endif // _HAS_CXX20
        return _Try_lock_until(_Abs_time);
    }

    template <class _Rep, class _Period>         // try to lock for duration
    bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time)
    { 
        return try_lock_until( _To_absolute_time(_Rel_time) );
    }
    // 函 _To_absolute_time 的返回值类型是 time_point <  steady_clock , duration<long_long,nano>  >
    // auto _To_absolute_time (const chrono::duration<_Rep, _Period>& _Rel_time ) 

};

++ 给出举例:

在这里插入图片描述

++ 递归锁,不能用 unique_lock 包装,否则会出错

在这里插入图片描述

(44) unique_lock 不管理递归锁 ,原因如下:

在这里插入图片描述

++ 再举例:

在这里插入图片描述

(45)

谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangzhangkeji

谢谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值