muduo网络库net篇二:定时器

Timer、TimerId类

Timer类主要成员数据:

  const TimerCallback callback_; //回调函数
  Timestamp expiration_; //定时器超时时间
  const double interval_; //定时器超时时间
  const bool repeat_;  //interval_>0
  const int64_t sequence_; //定时器Id

Timer类提供定时器回调函数,管理定时器时间
TimerId类更为简单

  Timer* timer_;
  int64_t sequence_;

创建对象时应TimerId.sequence_=Timer.sequence_,其实就是存储Timer和Id的对应关系。取消事件时会用到。

TimerQueue类

定时函数的择取问题,陈硕已经讲的比较清晰 详见 定时器。
timerfd_* 的linux API:参考1 参考2

int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);

TimerQueue类主要成员数据:

  EventLoop* loop_;
  const int timerfd_;
  Channel timerfdChannel_;
  TimerList timers_;

  ActiveTimerSet activeTimers_;
  ActiveTimerSet cancelingTimers_;

timerfd_:即为timerfd_create返回的文件描述符,其事件监听与EventLoop.wakeupChannel_过程一致,不赘述。
loop_:老朋友又出现了。
注册定时器函数,最终实际上是调了

loop_->runInLoop(...);

前文已经说过EventLoop.loop()完成事件监听和调度,显然TimerQueue对象并不需要也不应该单独创建,而是隶属于某一个loop_的。

boost::scoped_ptr timerQueue_;  //EventLoop

TimerQueue类成员函数结合实际来分析
首先创建EventLoop对象,通过run(…)设置定时器、注册定时器回调函数。同Channel事件的回调函数一样,定时器回调函数的调用线程也必须是创建loop_的线程。

EventLoop->TimerQueue:addTimer(...)
TimerQueue->EventLoop:runInLoop(...)

当调度时,会插入timers_和activeTimers_集合中,timers_是按实际调度时间排序的,如果是最先触发的,则立即设置定时器时间。
定时器超时调用TimerQueue::handleRead(timerQueue_.timerfdChannel_的回调函数),获取超时定时器集合expired,并从timers_和activeTimers_中删除(并非delete Timer),然后一个个调用定时器回调函数。之后在expired中的循环定时器新时间插入到timers_和activeTimers_,并为timers_中最先触发的设置定时器时间。

小结

muduo定时器实际上也是通过poll/epoll实现调度的。
TimerQueue与EventLoop有裙带关系,实际使用时,仅仅是通过EventLoop加入定时器事件时,取消定时器。
TimerQueue内部使用std::set存储定时器,当定时器超时则timerfd可读,这时会调用TimerQueue::handleRead,进一步会调用Timer回调函数。
只有加入定时器和定时器超时时,才会有可能设置timerfd新的超时时间。

有一个问题….
我认为取消定时器可以从timers_和activeTimers_中删除定时器,却不能设置cancelingTimers_。因为加入或取消定时器的实际调度都需要loop的当前线程,callingExpiredTimers_不会在取消定时器函数中为true。


你可能感兴趣的:(muduo网络库)