Muduo 定时器

TimeQueue定时器

图片转载自:muduo网络库源码解析(4):TimerQueue定时机制_李兆龙的技术博客_51CTO博客

muduo网络库源码解析(4):TimerQueue定时机制_事件分发机制

添加新的定时器

TimerId TimerQueue::addTimer(TimerCallback cb,	//用户自定义回调
                             Timestamp when,	  //定时器的超时时刻
                             double interval)	  //重复触发间隔,小于0则不重复触发
{
  Timer* timer = new Timer(std::move(cb), when, interval);	//回调函数交由timer保管
  loop_->runInLoop(
      std::bind(&TimerQueue::addTimerInLoop, this, timer));
  return TimerId(timer, timer->sequence());
}

通过addTImer添加一个定时器(实际调用的是addTimerInLoop),在runInLoop中注册一个函数addTimerInLoop,

之后在EventLoop的runInLoop中会把该函数放入pedingFunctor中,在loop中调用执行.

当然runInloop并不只有这么简单.

删除定时器

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

也是一样把实际做删除操作的函数放入pedingFunctor,在loop中调用执行

TimerQueue::TimerQueue(EventLoop* loop)
  : loop_(loop),	//当前TimeQueue属于某个EventLoop
    timerfd_(createTimerfd()),	//初始化一个timefd
    timerfdChannel_(loop, timerfd_),	//初始化一个关注timerfd的channel
    timers_(),	//保存所有的定时器
    callingExpiredTimers_(false)
{
  timerfdChannel_.setReadCallback(
      std::bind(&TimerQueue::handleRead, this));	//为timerfd注册读回调
  // we are always reading the timerfd, we disarm it with timerfd_settime.
  timerfdChannel_.enableReading();	//使timerfd读可用
}

在构造函数中有一个timerfd,这是一个文件描述符,当它被设置的超时时间到了后变为可读,所以它可以被pool/epoll监听.

设置了超时后调用的回调函数 handleRead

timerfd可读后触发的回调函数

void TimerQueue::handleRead()
{
  loop_->assertInLoopThread();
  Timestamp now(Timestamp::now());//获取当前时间
  readTimerfd(timerfd_, now);	//读取超时时间,因为muduo默认LT 防止多次触发

  std::vector<Entry> expired = getExpired(now);	//获取目前超时的timer,并且移动给expired

  callingExpiredTimers_ = true;	//处理超时事件
  /*
  	在处理超时事件时,如果超时的timer被删除了(也就是调用了cancelInLoop),那么为了保证超时回调正确执行,
  	会把它加入到待删除队列中,再在reset()函数中对待删除队列执行操作(cancelingTimers_)
  */

  cancelingTimers_.clear();	//清空删除队列,这里面的timer已经在reset中被delete了

  // safe to callback outside critical section
  for (const Entry& it : expired)
  {
    it.second->run();		//通过timer调用用户传入的超时回调
  }
  callingExpiredTimers_ = false;	//超时事件处理完成

  reset(expired, now);
}

在handleRead()中,通过getExpired()读取到了所有超时的定时器并返回vector维护,之后依次处理这些超时的定时器,并且调用这些超时定时器的回调

可以看到 callingExpiredTimers_ 在处理超时事时被设置为了 true 处理完成后则设置为 false

因为在处理超时事件时,定时器已经被移动到了expired中,如果此时在对其进行删除,在timers_和activeTimers_中
是找不到的,所以通过cancelingTimers_保存这些定时器,等到超时事件处理完成再去reset()中处理.

取消定时器实际调用cancelInLoop

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())
  {
    size_t n = timers_.erase(Entry(it->first->expiration(), it->first));	//在timers_中删除定时器
    assert(n == 1); (void)n;
    delete it->first;  /*在activeTimers_中删除定时器(应为是裸指针,所以delete,
    					 并且timers_和activeTimers_中timer_是同一个指针)*/
    activeTimers_.erase(it);
  }
  else if (callingExpiredTimers_)	//判断是否正在处理超时事件
  {
    cancelingTimers_.insert(timer);		
  }
  assert(timers_.size() == activeTimers_.size());		
}

添加定时器的实际调用

void TimerQueue::addTimerInLoop(Timer* timer)
{
  loop_->assertInLoopThread();		//判断是否在IO线程中
  bool earliestChanged = insert(timer);	//插入Timers_和activeTimers_(cancel)
 
  if (earliestChanged)
  {
  	//如果timers中最低的时间限度被更新,就更新一次超时时刻
    resetTimerfd(timerfd_, timer->expiration());
  }
}
bool TimerQueue::insert(Timer* timer)
{
  loop_->assertInLoopThread();
  assert(timers_.size() == activeTimers_.size());
  bool earliestChanged = false;
  Timestamp when = timer->expiration();		//获取传入的timer的超时时刻

  /*获取队列中定时时间最短的项,即第一个 因为数据结构是set,红黑树有序,
  比较顺序为pair的比较顺序 即先比较first,相同比较second*/
  TimerList::iterator it = timers_.begin();
  if (it == timers_.end() || when < it->first)//timers中不存在任何的Timer或者传入的timer超时时刻小于最小的那一个
  {
    earliestChanged = true;	//刚刚插入的timer是定时器队列中的超时时刻最短时间内被触发的
  }
    
  {
    std::pair<TimerList::iterator, bool> result
      = timers_.insert(Entry(when, timer));//插入set容器, when:超时时间,  timer:定时器
      //就算Timestamp(超时时间)一样后面的地址(timer,这是一个指针)也一定不一样
     
    assert(result.second); (void)result;	//断言,永真
  }
    
  {
  	//插入activeTimers_容器
    std::pair<ActiveTimerSet::iterator, bool> result
      = activeTimers_.insert(ActiveTimer(timer, timer->sequence()));
    assert(result.second); (void)result;
  }

  assert(timers_.size() == activeTimers_.size());	//两个容器中的定时器数量要一致
  return earliestChanged;	//是否把timer插入到set中
}


获取超时定时器getExpired()

std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{
  assert(timers_.size() == activeTimers_.size());
  std::vector<Entry> expired;
  //UINTPTR_MAX为 uintptr_t 类型对象的最大值
  //“reinterpret_cast 运算符并不会改变括号中运算对象的值,而是对该对象从位模式上进行重新解释”
  Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));


  // 返回第一个未到期的Timer的迭代器,因为set是有序的,所以在它前面的tiemr都是到期的
  // lower_bound的含义是返回第一个值>=sentry的元素的iterator
  // 即*end >= sentry,从而end->first > now
  // 注意:此处是>,而不是>=
  TimerList::iterator end = timers_.lower_bound(sentry);	
  assert(end == timers_.end() || now < end->first);

  //将[being(),end)之间的元素(到期的)追加到expired的末尾
  std::copy(timers_.begin(), end, back_inserter(expired));	


  timers_.erase(timers_.begin(), end);	//删除刚刚复制的定时器

  for (const Entry& it : expired)
  {
  	// 从activeTimers_中移除到期的定时器
    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;
}

reset函数

void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
{
  Timestamp nextExpire;

  for (const Entry& it : expired)
  {
    ActiveTimer timer(it.second, it.second->sequence());//timer sequence
    if (it.second->repeat()	//有设置超时时间
        && cancelingTimers_.find(timer) == cancelingTimers_.end())	//超时的timer,不在待删除队列中
    {
      it.second->restart(now);	//重置超时时间
      insert(it.second);		//重新加入timers_ 和 activeTimers_监听超时事件
    }
    else	//没有设置超时时间,或者刚刚超时的timer在执行超时回调时被加入了删除队列
    {
      // FIXME move to a free list
      delete it.second; // FIXME: no delete please 删除timer指针
    }
  }

  if (!timers_.empty())	
  {
    nextExpire = timers_.begin()->second->expiration();	//最小的期望时间数
  }

  if (nextExpire.valid())	//最小的时间项数有效的话
  {
    resetTimerfd(timerfd_, nextExpire);	//重置定时器
  }
}


resetTimerfd()函数

// 重置定时器的超时时间,用到了timerfd_settime() expiration:用户定时器的超时时间

void resetTimerfd(int timerfd, Timestamp expiration)
{
  // wake up loop by timerfd_settime()
  struct itimerspec newValue;
  struct itimerspec oldValue;
  memZero(&newValue, sizeof newValue);
  memZero(&oldValue, sizeof oldValue);
  newValue.it_value = howMuchTimeFromNow(expiration);	//设置超时时间,未设置it_interva,定时器定时一次
  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);//重新设置timerfd的超时时刻
  if (ret)
  {
    LOG_SYSERR << "timerfd_settime()";
  }
}

用户传入的timer被保存在timers_中,设置一个timerfd和对应的channel,它超时后调用handleread(),在这里面获取所有超时的定时器,并且依次执行这些定时器的回调函数(用户传入),那么这个**timerfdChannel__**就应该时常被触发

找到timerfd固定的超时时间

在addTimerInLoop()中,如果新加入的定时器的超时时刻,在定时器队列中是最小的那个,那么把新加入的超时时刻与之前timerfd的超时时刻取差值 作为timefd_的新的超时时刻

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值