muduo库分析——net篇(3)Timer定时器相关

定时器是基于EventLoop

所有定时器都注册在一个定时器IO上,定时器IO在到达指定时间后发生可读事件,EventLoop读该链接并调用当前已过期的所有定时器的函数,这些函数都属于EventLoop中pendingFunctors_


首先看定时器Timer:

成员:

  const TimerCallback callback_;    //绑定的回调函数
  Timestamp expiration_;      //指定的时间戳
  const double interval_;    //循环次数
  const bool repeat_;        //是否重复
  const int64_t sequence_;    //定时器ID

  static AtomicInt64 s_numCreated_;    //定时器个数
只定义了一个函数,作用是如果重复定时器的话,在执行了相关回调函数后将添加到定时器队列中等待下一次触发
void Timer::restart(Timestamp now)
{
  if (repeat_)
  {
    expiration_ = addTime(now, interval_);
  }
  else
  {
    expiration_ = Timestamp::invalid();
  }
}


TimerID:

这是一个封装定时器和其ID的类


TimerQueue:

成员:

首先要知道set数据结构是有序的,按照从小到大排列,如果是指定成员为pair时,排序先按照S大小,再按照X大小排列

// so that we can find an T* in a set>.
  typedef std::pair Entry;
  typedef std::set TimerList;
  typedef std::pair ActiveTimer;
  typedef std::set ActiveTimerSet;

  void addTimerInLoop(Timer* timer);    
  void cancelInLoop(TimerId timerId);
  // called when timerfd alarms
  void handleRead();
  // move out all expired timers
  std::vector getExpired(Timestamp now);
  void reset(const std::vector& expired, Timestamp now);

  bool insert(Timer* timer);

  EventLoop* loop_;    //线程绑定的loop对象
  const int timerfd_;    //定时器IO描述符
  Channel timerfdChannel_;    //定时器链接
  // Timer list sorted by expiration
  TimerList timers_;		//默认按照时间排序

  // for cancel()
  ActiveTimerSet activeTimers_;		//默认按照地址排序
  bool callingExpiredTimers_; /* atomic */
  ActiveTimerSet cancelingTimers_;	//需要取消的定时器

具体实现:

int createTimerfd()		//创建定时器描述符
struct timespec howMuchTimeFromNow(Timestamp when)	//Timestamp转换为timespec
void readTimerfd(int timerfd, Timestamp now)	//从定时器IO中读取信息,作为回调函数,读后防止水平触发
void resetTimerfd(int timerfd, Timestamp expiration)	//将定时器IO重新设置时间
TimerId TimerQueue::addTimer(const TimerCallback& cb,	//非本线程调用
                             Timestamp when,
                             double interval)
{
  Timer* timer = new Timer(cb, when, interval);
  //添加到IO线程中等待被调用,线程安全的不需要锁
  loop_->runInLoop(
      boost::bind(&TimerQueue::addTimerInLoop, this, timer));
  return TimerId(timer, timer->sequence());
}

先看runInLoop函数:

void EventLoop::runInLoop(Functor&& cb)
{
  if (isInLoopThread())    //判断是否在本线程,如果是则直接调用函数
  {
    cb();
  }
  else
  {
    queueInLoop(std::move(cb));
  }
}

如果不是IO线程调用的话,需要添加到pendingFunctors_中,等待统一的调用,并且如果是其他线程就要唤醒,或者正在处于统一的调用中时需要唤醒,因为此次添加的函数在此时不会立即执行,因为有两个队列,一个是pendingFunctors_,另一个是执行中pendingFunctors_的副本,添加的函数在pendingFunctors中,其副本不会添加,执行的是副本中的函数,具体原因后面有

void EventLoop::queueInLoop(Functor&& cb)
{
  {
  MutexLockGuard lock(mutex_);
  pendingFunctors_.push_back(std::move(cb));  // emplace_back
  }

  if (!isInLoopThread() || callingPendingFunctors_)
  {
    wakeup();
  }
}

涉及到定时器的操作添加删除都是由创建定时器的那个IO线程进行,而其他线程需要添加定时器时,需要将其设置为事件使得IO线程进行添加删除操作

void TimerQueue::addTimerInLoop(Timer* timer)	//在本线程中调用
{
  loop_->assertInLoopThread();
  bool earliestChanged = insert(timer);	//增加到定时器集合和活跃的定时器集合中,
										//如果时间比之前已有定时器短则返回True

  if (earliestChanged)
  {
    resetTimerfd(timerfd_, timer->expiration());	//当前时间需要修改时,更改定时器IO的时间
  }
}


删除:

void TimerQueue::cancel(TimerId timerId)
{
  loop_->runInLoop(
      boost::bind(&TimerQueue::cancelInLoop, this, timerId));
}


void TimerQueue::cancelInLoop(TimerId timerId)
{
  loop_->assertInLoopThread();
  assert(timers_.size() == activeTimers_.size());
  ActiveTimer timer(timerId.timer_, timerId.sequence_);	//转换为Timer
  ActiveTimerSet::iterator it = activeTimers_.find(timer);	//查找到活跃定时器指针
  if (it != activeTimers_.end())
  {
    size_t n = timers_.erase(Entry(it->first->expiration(), it->first));	//删除定时器集合中的Timer
    assert(n == 1); (void)n;
    delete it->first; // FIXME: no delete please 释放Timer
    activeTimers_.erase(it);	//删除活跃定时器中Timer
  }
  else if (callingExpiredTimers_)	//正在调用过期定时器时,添加进cancelingTimers
  {
    cancelingTimers_.insert(timer);
  }
  assert(timers_.size() == activeTimers_.size());
}

定时器IO回调函数:

void TimerQueue::handleRead()	//定时器回调函数
{
  loop_->assertInLoopThread();
  Timestamp now(Timestamp::now());
  readTimerfd(timerfd_, now);	//读取事件,如果不读取由于Epoll水平触发会导致一直有IO事件产生

  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);	//如果有时间间隔的定时器需要重新添加进定时器
}
std::vector TimerQueue::getExpired(Timestamp now)	//获得已过期的定时器
{
  assert(timers_.size() == activeTimers_.size());			
  std::vector expired;
  Entry sentry(now, reinterpret_cast(UINTPTR_MAX));		//新建一个当前时间定时器,
																//用UINTPTR_MAX原因是默认排序
																//如果时间相等则地址大小排序,取最大防止漏掉定时器
  TimerList::iterator end = timers_.lower_bound(sentry);	//使用指针找到当前时间最后一个定时器(第一个>=sentry的定时器)
  assert(end == timers_.end() || now < end->first);
  std::copy(timers_.begin(), end, back_inserter(expired));	//将开始到现在的定时器复制到expired返回,end之前不包括end
  timers_.erase(timers_.begin(), end);		//删除这一范围定时器

  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;
}

现在看EventLoop中事件处理函数(一般称为计算函数相对于IO处理函数):

这是Loop的调用,其中前面都是eventHanding状态(IO处理)

之前讲过,现在看最后一个调用doPendingFunctors

while (!quit_)
  {
    activeChannels_.clear();	//清理活跃中的IO通道
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);	//等待Poller将活跃的IO通道传送过来
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();	//输出通道
    }
    // TODO sort channel by priority
    eventHandling_ = true;	//处理事件状态
    for (ChannelList::iterator it = activeChannels_.begin();	//遍历每个IO通道处理相关事件
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();	//调用任务处理函数
  }

这是callingPendingFunctors_(计算处理),因为服务器一般分为两个过程,IO处理,cpu处理,前者用于IO相关事件处理,后者用于计算时间比较长的相关事件处理,两者分开更适合分布式网络的一般情况(高IO,低延迟,快速计算,分布集群等等)

void EventLoop::doPendingFunctors()
{
  std::vector functors;	//任务副本,防止任务调用queueInLoop陷入死循环
  callingPendingFunctors_ = true;	//设置当前Loop处于任务处理状态

  {
  MutexLockGuard lock(mutex_);		//加锁取出任务到副本,并清空任务队列
  functors.swap(pendingFunctors_);	//之所以局部加锁是因为防止任务调用queueInLoop陷入死锁
  }

  for (size_t i = 0; i < functors.size(); ++i)
  {
    functors[i]();
  }
  callingPendingFunctors_ = false;
}

这里pendingFuntors还有个副本,是因为

1.一旦调用中还有添加事件到队列中不会引起死循环

2.lock在swap范围外包裹了循环的话,调用可能引起加锁解锁,很可能发生死锁状态


你可能感兴趣的:(c++)