<mutex>注释 12:重新思考与猜测、补充锁的睡眠与唤醒机制,结合 linux0.11 操作系统代码的辅助(下)
(60)继续分析,为什么 timed_mutex 可以拥有准时的等待时间:
++逐步测试:
++ 以及:
++以及:
++ 以及:
++ 上面的例子里之所以这么编写。无论 timed_mutex 里的定时等待函数,还是 条件变量 condition_variable 里的限时 wait_xx (…) 函数。最终都是调用上面的函数完成定时等待。所以手工为其凑齐了参数。直接调用了这个函数。
(61) 列出简化版的 mutex 与 condition_variable 的源代码:
++ 以及:
++ 根据上面的大量的测试,结合 linux 0.11 上的进程睡眠与唤醒的规律,推测一下上图中这些不同底层函数的功能。锁 mutex 的 加锁解锁,应该属于 不可中断睡眠状态,task_uninterruptable 。这类睡眠由 mutex . unlock () 来唤醒等待锁的线程。 条件变量是为了线程同步,避免线程的无意义的无序竞争锁,比如同一文件的读写线程之间的竞争,此情形就可以由 条件变量来规范线程的睡眠与唤醒。 调用了 condition_variable . wait ()的线程也会进入睡眠状态,但不是在锁上睡眠,而是在条件变量上睡眠,只可以由对应的 condition_variable . notify ()等函数来唤醒线程,然后这些被唤醒的线程再去竞争锁。所以在条件变量上睡眠的线程也属于在不可中断睡眠状态 task_uninterruptable 。因为 锁有 80 字节的内存,条件变量有 72 字节的内存,所以锁与条件变量都可以记录其上的睡眠线程的 id 的,或者是 线程控制块的地址 TCB ,以便于将来唤醒这些线程。 条件变量上的定时睡眠,应该属于 可中断睡眠状态 task_interruptable , 因为该线程要响应定时信号 SIGALRM ,以便在定时到时由操作系统唤醒,改为可运行状态 task_running 。但定时睡眠后,(包括 条件变量上的无限睡眠状态 condition_variable . wait ()),从条件变量上醒来后的线程,都要去竞争锁 mutex ,得不到锁的线程,还会再次进入睡眠状态,改为在 锁 mutex 上睡眠。下一次就不是条件变量来唤醒了,而是由 锁 mutex . unlock() 来唤醒。而且也对应了另一句话,在条件变量上睡眠的线程在调用 wait()前必须先持有锁。由 条件变量释放锁后把线程放在条件变量上睡眠,条件变量上的睡眠线程,醒来后的第一件事就是参与锁的竞争。条件变量上的限时睡眠,也可以被提前唤醒,但若断言条件不满足,还会继续睡眠,直到把指定的时间用完。
(62)至此,基本理清了定时锁 timed_mutex 为什么具有精确的醒来功能了,不会超时睡眠。因为 timed_mutex 中的 锁
timed_mutex . _My_mutex 是一直闲置的,不经常被加锁,所以定时睡眠的线程醒来后一定可以拿到这个锁。但 timed_mutex . _My_locked 这个整型数据的是否为 0 的语义,才表示临界资源是否空闲。锁 timed_mutex . _My_mutex 是为了在写 timed_mutex . _My_locked 时保护一下这个整形数据,然后就释放锁了。有点类似于双重锁定的功能与感觉。
类似的结构体设计,还有线程间共享数据,共享函数返回值的结构 _Associated_state《T》:
++ 继续:
++ 也给出上面图的代码版本:
template <class _Ty>
struct _State_manager // class future : public _State_manager<_Ty>
{
_Associated_state<_Ty>* _Assoc_state;
bool _Get_only_once;
// 当 本模板 作为 future 的父类时,_Get_only_once 为 true ;
// 当 本模板 作为 shared_future 的父类时,_Get_only_once 为 false
void wait() { _Assoc_state->_Wait(); }
template <class _Rep, class _Per> // wait for duration
future_status
wait_for(const chrono::duration<_Rep, _Per>& _Rel_time)
{ return _Assoc_state->_Wait_for(_Rel_time); }
template <class _Clock, class _Dur> // wait until time point
future_status
wait_until(const chrono::time_point<_Clock, _Dur>& _Abs_time)
{ return _Assoc_state->_Wait_until(_Abs_time); }
};
template <class _Ty>
class _Associated_state //用于管理关联同步状态的类
{
virtual void _Wait() {
unique_lock<mutex> _Lock(_Mtx);
_Maybe_run_deferred_function(_Lock);
while (!_Ready) _Cond.wait(_Lock);
}
template <class _Rep, class _Per> // wait for duration
future_status _Wait_for( chrono::duration<_Rep, _Per>& _Rel_time) {
unique_lock<mutex> _Lock(_Mtx);
if (_Has_deferred_function()) return future_status::deferred;
if (_Cond.wait_for(_Lock, _Rel_time, _Test_ready(this)))
return future_status::ready;
return future_status::timeout;
}
template <class _Clock, class _Dur> // wait until time point
future_status _Wait_until( chrono::time_point<_Clock, _Dur>& _Abs_time) {
unique_lock<mutex> _Lock(_Mtx);
if (_Has_deferred_function()) return future_status::deferred;
if (_Cond.wait_until(_Lock, _Abs_time, _Test_ready(this)))
return future_status::ready;
return future_status::timeout;
}
};
(63)
谢谢