muduo : Timer

关于timerfd : http://blog.csdn.net/huntinux/article/details/52095663

Timer

成员变量

 private:
  const TimerCallback callback_; // 回调函数
  Timestamp expiration_;         // 过期时间
  const double interval_;
  const bool repeat_;            // 是否重复
  const int64_t sequence_;       // 当前定时器的编号(基于s_numCreated_)

  static AtomicInt64 s_numCreated_; // 记录定时器的个数

成员函数

调用回调函数

  void run() const
  {
    callback_();
  }

重启定时器

void Timer::restart(Timestamp now)
{
  if (repeat_)
  {
    expiration_ = addTime(now, interval_);
  }
  else
  {
    expiration_ = Timestamp::invalid();
  }
}

TimerId

///
/// An opaque identifier, for canceling Timer.
///
class TimerId : public muduo::copyable
{
 public:
  TimerId()
    : timer_(NULL),
      sequence_(0)
  {
  }

  TimerId(Timer* timer, int64_t seq)
    : timer_(timer),
      sequence_(seq)
  {
  }

  // default copy-ctor, dtor and assignment are okay

  friend class TimerQueue;

 private:
  Timer* timer_;
  int64_t sequence_;
};

Timestamp

比较简单,它继承了boost::less_than_comparable。关于boost::less_than_comparable的使用见这里。

TimerQueue

muduo使用timerfd来管理定时器,使得对timer的管理与对socket fd 的管理统一了起来。 (《Linux高性能服务器编程》中提到了统一事件源)

TimerQueue用来管理Timer,功能包括:

  • 高效的组织目前未到期的Timer
  • 能快速根据当前时间找到已经到期的Timer
  • 能高效地添加或删除Timer

muduo没有使用map来管理,原因是这么做无法处理两个Timer到期时间相同的情况。为了解决这个问题,muduo对key进行了区分,每个key是一个pair,这么做就能保证即便到期时间相同也可以区分。

  typedef std::pair Entry; // key用pair表示,可以区分到期时间相同的情况
  typedef std::set TimerList; // 使用std::set管理Timer,因为只有key没有value,无需使用map

  // Timer list sorted by expiration
  TimerList timers_;

并且,TimeQueue只在一个线程内使用,所以无需同步控制。

此外,比较重要的一点是,muduo使用一个timerfd管理多个timers_set> timers_),该timerfd的超时时间是集合timers_中的最小超时时间, 即timers_.begin()->second->expiration()timers_.begin()->first,因为set是按照key排序的,第一个定时器timer的超时时间就是最小超时时间。

与EventLoop的关系

TimerQueue使用一个Channel来观察timerfd_上的readable事件。Channel是在构造函数设置了回调函数。即,timerfd变为readable时(定时器超时),会调用TimerQueue::handleRead

TimerQueue::TimerQueue(EventLoop* loop)
  : loop_(loop),
    timerfd_(createTimerfd()),
    timerfdChannel_(loop, timerfd_),
    timers_(),
    callingExpiredTimers_(false)
{
  timerfdChannel_.setReadCallback(
      boost::bind(&TimerQueue::handleRead, this));
  // we are always reading the timerfd, we disarm it with timerfd_settime.
  timerfdChannel_.enableReading();
}
void TimerQueue::handleRead()
{
  loop_->assertInLoopThread();
  Timestamp now(Timestamp::now());
  readTimerfd(timerfd_, now);

  // 查询到期的Timer
  std::vector expired = getExpired(now);

  callingExpiredTimers_ = true;
  cancelingTimers_.clear();
  // safe to callback outside critical section
  for (std::vector::iterator it = expired.begin();
      it != expired.end(); ++it)
  {
    it->second->run(); // 调用相应的回调函数
  }
  callingExpiredTimers_ = false;

  reset(expired, now);
}

过期查询

返回值虽然是return-by-value,但是作者提到了编译器会进行RVO优化,不用担心性能。(详见C++ FAQ)

std::vector TimerQueue::getExpired(Timestamp now)
{
  assert(timers_.size() == activeTimers_.size());
  std::vector expired;
  Entry sentry(now, reinterpret_cast(UINTPTR_MAX)); // 哨兵值
  TimerList::iterator end = timers_.lower_bound(sentry); // 返回第一个未到期的Timer
  assert(end == timers_.end() || now < end->first);
  std::copy(timers_.begin(), end, back_inserter(expired)); // 将所有到期的Timer通过back_inserter放到容器expired中
  timers_.erase(timers_.begin(), end); // 删除已经到期的Timer

  for (std::vector::iterator it = expired.begin();
      it != expired.end(); ++it)
  {
    ActiveTimer timer(it->second, it->second->sequence());
    size_t n = activeTimers_.erase(timer);
    assert(n == 1); (void)n;
  }

  assert(timers_.size() == activeTimers_.size());
  return expired;
}

你可能感兴趣的:(muduo)