(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)
谢谢