Muduo源码笔记系列:
muduo源码阅读笔记(0、下载编译muduo)
muduo源码阅读笔记(1、同步日志)
muduo源码阅读笔记(2、对C语言原生的线程安全以及同步的API的封装)
muduo源码阅读笔记(3、线程和线程池的封装)
muduo源码阅读笔记(4、异步日志)
muduo源码阅读笔记(5、Channel和Poller)
muduo源码阅读笔记(6、ExevntLoop和Thread)
muduo源码阅读笔记(7、EventLoopThreadPool)
muduo源码阅读笔记(8、定时器TimerQueue)
muduo源码阅读笔记(9、TcpServer)
muduo源码阅读笔记(10、TcpConnection)
前言
为了方便Poller的管理,Muduo定时器是基于文件描述符实现。
实现 定时器提供的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class TimerQueue : noncopyable{public : explicit TimerQueue (EventLoop* loop) ; ~TimerQueue (); TimerId addTimer (TimerCallback cb, Timestamp when, double interval) ; void cancel (TimerId timerId) ; private : typedef std::pair<Timestamp, Timer*> Entry; typedef std::set<Entry> TimerList; typedef std::pair<Timer*, int64_t > ActiveTimer; typedef std::set<ActiveTimer> ActiveTimerSet; void addTimerInLoop (Timer* timer) ; void cancelInLoop (TimerId timerId) ; void handleRead () ; std::vector<Entry> getExpired (Timestamp now) ; void reset (const std::vector<Entry>& expired, Timestamp now) ; bool insert (Timer* timer) ; EventLoop* loop_; const int timerfd_; Channel timerfdChannel_; TimerList timers_; ActiveTimerSet activeTimers_; bool callingExpiredTimers_; ActiveTimerSet cancelingTimers_; };
构造函数:
在每个EventLoop创建时,在自己的构造函数中,创建自己的定时器TimerQueue
,并将EventLoop的this指针作为TimerQueue构造函数的参数。TimerQueue的构造会创建一个timerfd,并且向EventLoop的Poller注册timerfd。这样,Poller正式开开始管理定时器。后面的Acceptor、TcpConnection使用了类似的手法。
实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 TimerQueue::TimerQueue (EventLoop* loop) : loop_ (loop), timerfd_ (createTimerfd ()), timerfdChannel_ (loop, timerfd_), timers_ (), callingExpiredTimers_ (false ) { timerfdChannel_.setReadCallback ( std::bind (&TimerQueue::handleRead, this )); timerfdChannel_.enableReading (); }
关于<号的万能性
将自定义类存入std::set是要求用户实现自定义对象<号重载的。思考一个问题:只重载<的话,如果用户调用find成员函数时,set如何判断两个对象是否相等呢?
其实std::set内部做两次比较即可判断两个对象是否相等。方法:当a < b == false && b < a == false时,说明此时 a == b。读者可以在这里仔细思考一下。Timestamp正是因为实现了<才可以作为std::set的元素类型。
一个自定义对象重载<号后,不光可以通过<推导出==,还可以推到出>、>=、<=号。参考博客
参考boost::less_than_comparable的实现,如下:
1 2 3 4 5 6 7 8 9 10 friend bool operator <(const T& x, const T& y) { }friend bool operator >(const T& x, const T& y) { return y < x; }friend bool operator <=(const T& x, const T& y) { return !static_cast <bool >(y < x); }friend bool operator >=(const T& x, const T& y) { return !static_cast <bool >(x < y); }
定时器实现的伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 TimerId TimerQueue::addTimer (TimerCallback cb, Timestamp when, double interval) { Timer* timer = new Timer (std::move (cb), when, interval); loop_->runInLoop ( std::bind (&TimerQueue::addTimerInLoop, this , timer)); return TimerId (timer, timer->sequence ()); } void TimerQueue::cancel (TimerId timerId) { loop_->runInLoop ( std::bind (&TimerQueue::cancelInLoop, this , timerId)); } void TimerQueue::addTimerInLoop (Timer* timer) { loop_->assertInLoopThread (); bool earliestChanged = insert (timer); if (earliestChanged){ resetTimerfd (timerfd_, timer->expiration ()); } } void TimerQueue::cancelInLoop (TimerId timerId) { loop_->assertInLoopThread (); assert (timers_.size () == activeTimers_.size ()); ActiveTimer timer (timerId.timer_, timerId.sequence_) ; ActiveTimerSet::iterator it = activeTimers_.find (timer); if (it != activeTimers_.end ()){ delete it->first; }else if (callingExpiredTimers_){ cancelingTimers_.insert (timer); } assert (timers_.size () == activeTimers_.size ()); } void TimerQueue::handleRead () { loop_->assertInLoopThread (); Timestamp now (Timestamp::now()) ; readTimerfd (timerfd_, now); std::vector<Entry> expired = getExpired (now); callingExpiredTimers_ = true ; cancelingTimers_.clear (); for (const Entry& it : expired){ it.second->run (); } callingExpiredTimers_ = false ; reset (expired, now); } std::vector<TimerQueue::Entry> TimerQueue::getExpired (Timestamp now) { assert (timers_.size () == activeTimers_.size ()); std::vector<Entry> expired; for (const Entry& it : expired){ } assert (timers_.size () == activeTimers_.size ()); return expired; } void TimerQueue::reset (const std::vector<Entry>& expired, Timestamp now) { Timestamp nextExpire; for (const Entry& it : expired){ ActiveTimer timer (it.second, it.second->sequence()) ; if (it.second->repeat () && cancelingTimers_.find (timer) == cancelingTimers_.end ()){ it.second->restart (now); insert (it.second); }else { delete it.second; } } if (!timers_.empty ()){ nextExpire = timers_.begin ()->second->expiration (); } if (nextExpire.valid ()){ resetTimerfd (timerfd_, nextExpire); } } bool TimerQueue::insert (Timer* timer) { loop_->assertInLoopThread (); assert (timers_.size () == activeTimers_.size ()); bool earliestChanged = false ; Timestamp when = timer->expiration (); TimerList::iterator it = timers_.begin (); if (it == timers_.end () || when < it->first){ earliestChanged = true ; } return earliestChanged; }
细节明细: 疑问
定时器模块存在的意义?
解答
事件触发机制: 定时器在Muduo中被用作一种事件触发机制。通过设置定时器,用户可以在指定的时间间隔内执行相应的操作,例如执行定时任务、发送心跳包等。这种事件触发机制有助于异步编程中的任务调度和协调。
超时处理: 定时器用于处理超时事件,例如连接超时、读写操作超时等。通过设置合适的定时器,Muduo可以及时检测并处理超时情况,确保网络应用的稳定性和可靠性。
可能还不太全,后面再有所感悟再来更新。。。
本章完结