muduo的定时器由三个类实现,TimerId、Timer、TimerQueue。

1、采用timer_create函数得到定时器对象

timerfd_create把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便的融入到select/poll框架中,用统一的方式来处理I/O和超时事件,这正是Reactor模式的长处。

2、Timer类

Timer是对定时器的高层次抽象,封装了定时器的一些参数,例如超时回调函数、超时时间、超时时间间隔、定时器是否重复、定时器的序列号。其函数大都是设置这些参数,run()用来调用回调函数,restart()用来重启定时器(如果设置为重复)。

class Timer : boost::noncopyable
{public:void run() const  //run函数来调用回调函数{callback_();}Timestamp expiration() const  { return expiration_; }bool repeat() const { return repeat_; }int64_t sequence() const { return sequence_; }void restart(Timestamp now);static int64_t numCreated() { return s_numCreated_.get(); }private:const TimerCallback callback_;//超时回调函数Timestamp expiration_;        //超时时间const double interval_;       //超时时间间隔const bool repeat_;           //定时器是否重复const int64_t sequence_;      //定时器的序列号static AtomicInt64 s_numCreated_;
};//重复定时
void Timer::restart(Timestamp now)
{if (repeat_)//定时器是否重复{expiration_ = addTime(now, interval_);//计算超时时间}else{expiration_ = Timestamp::invalid();//return Timestamp()获取一个无效时间}
}

3、TimerQueue

TimerQueue的接口很简单,只有两个函数addTimer()和cancel()。它的内部有channel,和timerfd相关联。添加新的Timer后,在超时后,timerfd可读,会处理channel事件,之后调用Timer的回调函数;在timerfd的事件处理后,还有检查一遍超时定时器,如果其属性为重复还有再次添加到定时器集合中。

class TimerQueue : boost::noncopyable
{public:TimerQueue(EventLoop* loop);~TimerQueue();////// Schedules the callback to be run at given time,/// repeats if @c interval > 0.0.////// Must be thread safe. Usually be called from other threads.// 一定是线程安全的,可以跨线程调用。通常情况下被其它线程调用。TimerId addTimer(const TimerCallback& cb,Timestamp when,double interval);void cancel(TimerId timerId);private:// FIXME: use unique_ptr<Timer> instead of raw pointers.// unique_ptr是C++ 11标准的一个独享所有权的智能指针// 无法得到指向同一对象的两个unique_ptr指针// 但可以进行移动构造与移动赋值操作,即所有权可以移动到另一个对象(而非拷贝构造)typedef std::pair<Timestamp, Timer*> Entry;typedef std::set<Entry> TimerList;typedef std::pair<Timer*, int64_t> ActiveTimer;typedef std::set<ActiveTimer> ActiveTimerSet;// 以下成员函数只可能在其所属的I/O线程中调用,因而不必加锁。// 服务器性能杀手之一是锁竞争,所以要尽可能少用锁void addTimerInLoop(Timer* timer);void cancelInLoop(TimerId timerId);// called when timerfd alarmsvoid handleRead();// move out all expired timers// 返回超时的定时器列表std::vector<Entry> getExpired(Timestamp now);void reset(const std::vector<Entry>& expired, Timestamp now);bool insert(Timer* timer);EventLoop* loop_;        // 所属EventLoopconst int timerfd_;Channel timerfdChannel_;// Timer list sorted by expirationTimerList timers_;   // timers_是按到期时间排序// for cancel()// timers_与activeTimers_保存的是相同的数据// timers_是按到期时间排序,activeTimers_是按对象地址排序ActiveTimerSet activeTimers_;bool callingExpiredTimers_; /* atomic */ActiveTimerSet cancelingTimers_;  // 保存的是被取消的定时器
};//.cpp
void resetTimerfd(int timerfd, Timestamp expiration)
{// wake up loop by timerfd_settime()struct itimerspec newValue;struct itimerspec oldValue;bzero(&newValue, sizeof newValue);bzero(&oldValue, sizeof oldValue);newValue.it_value = howMuchTimeFromNow(expiration);int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);//启动和停止定时器;0表示相对时间,第三个参数视为相对于调用timerfd_settime()时间点的相对时间,第四个返回定时器的前一设置if (ret){LOG_SYSERR << "timerfd_settime()";}
}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;}
//插入两个时间列表中TimerList,ActiveTimerSet{std::pair<TimerList::iterator, bool> result= timers_.insert(Entry(when, timer));assert(result.second); (void)result;}{std::pair<ActiveTimerSet::iterator, bool> result= activeTimers_.insert(ActiveTimer(timer, timer->sequence()));assert(result.second); (void)result;}assert(timers_.size() == activeTimers_.size());return earliestChanged;
}//添加定时器到列表
void TimerQueue::addTimerInLoop(Timer* timer)
{loop_->assertInLoopThread();bool earliestChanged = insert(timer);//插入到定时列表中if (earliestChanged){resetTimerfd(timerfd_, timer->expiration());//定时列表为空或插入的时间比定时列表中的小更新定时器定时时间}
}//获取超时时间,并保存在vector中
std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{assert(timers_.size() == activeTimers_.size());std::vector<Entry> expired;Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));TimerList::iterator end = timers_.lower_bound(sentry);//最后一个小于指定时间的下一个位置assert(end == timers_.end() || now < end->first);std::copy(timers_.begin(), end, back_inserter(expired));//将满足超时的Entry拷贝到expired中timers_.erase(timers_.begin(), end);//在timers_列表中删除for (std::vector<Entry>::iterator it = expired.begin();it != expired.end(); ++it)                        //将满足超时条件的timer从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;   //返回满足超时的Entry的vector
}void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
{Timestamp nextExpire;//遍历超时时间数组,查找每一个timer是否设置为repeat且该定时器没有被放在cancelingTimers_中for (std::vector<Entry>::const_iterator it = expired.begin();it != expired.end(); ++it)     {ActiveTimer timer(it->second, it->second->sequence());if (it->second->repeat()&& cancelingTimers_.find(timer) == cancelingTimers_.end()){//满足条件,更新定时器启动时间it->second->restart(now);//将其放入列表中insert(it->second);}else  //不满足条件删除timer{// FIXME move to a free listdelete it->second; // FIXME: no delete please}}if (!timers_.empty())//最后判断timers_是否为空,不为空将第一个元素的值的expiration取出。{nextExpire = timers_.begin()->second->expiration();}if (nextExpire.valid()){resetTimerfd(timerfd_, nextExpire);}
}//chanel的回调函数,判断timerfd是可读,这也就是在插入定时时间是更新timerfd的原因
void TimerQueue::handleRead()
{loop_->assertInLoopThread();Timestamp now(Timestamp::now());//获取当前时间readTimerfd(timerfd_, now);//判断是否可读,主要是通过read(timerfd, &howmany, sizeof howmany)不可读则阻塞std::vector<Entry> expired = getExpired(now);//获取超时timer,callingExpiredTimers_ = true;cancelingTimers_.clear();// safe to callback outside critical sectionfor (std::vector<Entry>::iterator it = expired.begin();//对每一个超时的timer执行对应的回调函数it != expired.end(); ++it){it->second->run();}callingExpiredTimers_ = false;reset(expired, now);
}//取消
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));assert(n == 1); (void)n;delete it->first; // FIXME: no delete pleaseactiveTimers_.erase(it);}else if (callingExpiredTimers_) //若不在判断callingExpiredTimers_{cancelingTimers_.insert(timer);//说明其在超时处理中从activeTimers_中取出,因此,把它插入//cancelingTimers_}assert(timers_.size() == activeTimers_.size());
}

读Muduo源码笔记---8(定时器)相关推荐

  1. 读uCOSIII源码笔记

    首先谈一下自己读RTOS的粗略认识吧,刚开始接触单片机时使用的都是while(true),一个循环处理所有的任务.直到写了一个通过GPRS模块与上层服务通信(长连接)之后,才发现在while(true ...

  2. 【Muduo源码剖析笔记】 网络库之Acceptor、Connector

    [Muduo源码剖析笔记] 网络库之Acceptor.Connector Acceptor typedef std::function<void (int sockfd, const InetA ...

  3. 试读angular源码第三章:初始化zone

    直接看人话总结 前言 承接上一章 项目地址 文章地址 angular 版本:8.0.0-rc.4 欢迎看看我的类angular框架 文章列表 试读angular源码第一章:开场与platformBro ...

  4. muduo源码client/server通信流程

    今天来学习一下muduo源码中client和server间的大致通信流程,以echo服务为例,先看一下echo对面的main函数代码. #include "examples/simple/e ...

  5. 数据结构源码笔记(C语言):索引文件建立和查找

    //实现索引文件建立和查找算法#include<stdio.h> #include<malloc.h> #include<string.h> #include< ...

  6. 数据结构源码笔记(C语言):置换-选择算法

    //实现置换-选择算法#include<stdio.h> #include<malloc.h> #include<string.h> #include<std ...

  7. 读spring源码(一)-ClassPathXmlApplicationContext-初始化

    工作来几乎所有的项目都用到了spring,却一直没有系统的读下源码,从头开始系统的读下吧,分章也不那么明确,读到哪里记到哪里,仅仅作为个笔记吧. 先看ClassPathXmlApplicationCo ...

  8. vue如何让一句代码只执行一次_lt;Vue 源码笔记系列4gt;异步更新队列与$nextTick...

    1. 前言 原文发布在语雀: <Vue 源码笔记系列4>异步更新队列与$nextTick · 语雀​www.yuque.com 上一章我们讲到了修改数据是如何触发渲染函数的观察者,最终调用 ...

  9. 读 zepto 源码之工具函数

    对角另一面 读 zepto 源码之工具函数 Zepto 提供了丰富的工具函数,下面来一一解读. 源码版本 本文阅读的源码为 zepto1.2.0 $.extend $.extend 方法可以用来扩展目 ...

最新文章

  1. pycharm 链接wsl和 wsl 配置cuda nvidia
  2. ADO.NET实用经验 转载
  3. 本科主要数学科目的页数统计
  4. 官方的正则表达式组件 RegularExpressions (4) : 表达式选项
  5. 实变函数与泛函分析课本pdf_实变函数与泛函分析
  6. db2 dec函数oracle,DB2常用函数和Oracle的比较
  7. 还是有点香!苹果官翻2018款iPad Pro及MacBook Air上架
  8. string与stream互相转换
  9. Linux命令解释之du
  10. linux管道学习资料
  11. python之类和对象
  12. source-map讲解
  13. html双击变成可编辑状态,JS实现双击编辑可修改状态的方法
  14. sqlite入门基础(一):sqlite3_open,sqlite3_exec,slite3_close
  15. 虚拟机KALI2022.2下安装GVM
  16. Keepalived+Haproxy+Mysql(双主)高可用架构部署
  17. Android 装置CTS与GMS认证差异
  18. LightGBM详细
  19. Android开发--实现Android引导页
  20. Hello Lyq And Xj

热门文章

  1. 使用uni-app实现数字华容道小游戏
  2. 达林顿管 ULN2803
  3. android 扫码枪编程,Android扫描枪(PDA)开发
  4. Lock(四) 共享锁和排它锁
  5. 学习Linux这篇文章就够了
  6. 为什么Harris角点检测用特征值? 为什么Harris像素值变化量矩阵代表椭圆?【OpenCV】【计算机视觉】
  7. RK3399平台开发系列讲解(内核入门篇)1.2、如何高效的阅读Linux内核设备驱动
  8. 【ABAP】创建局部Macro和全局Macro
  9. 元宇宙退潮,人工智能起飞,大厂 Al 新赛点在哪?
  10. ASDL 三线共享教程