muduo源码分析之定时器TimerQueue
(muduo源码系列大多是我在看muduo源码的时候结合网上博客总结的,我尽可能多的是对源码注释)
简介
Muduo的定时器功能主要由三个class实现,TimerId,Timer,TimerQueue,TimerQueue的接口只有两个addTimer()和cancel(),addTimer()是提供给EventLoop使用的, EventLoop会把它封装成更好用的三个函数:runAt()、runAfter()、runEvery()。
大体实现
muduo 定时器封装了 Timer.h 里面保存的是超时时间和回调函数, TimerQueue.h 使用set容器保存多个定时器, 然后在TimerQueue中使用timerfd_create创建一个timerfd句柄, 插入定时器A后先比较A的触发时间和TimerQueue的触发时间, 如果A的触发时间比其小就使用timerfd_settime重置TimerQueue的timerfd的触发时间, TimerQueue中的timerfd的触发时间永远与保存的定时器中触发时间最小的那个相同, 然后timerfd触发可读后, 遍历保存的多个定时器, 看看有没有同时到期的, 有执行回调函数
TimerQueue的封装是为了让未到期的时间Timer有序的排列起来,这样,能够更具当前时间找到已经到期的Timer也能高效的添加和删除Timer。
所谓的到期与未到期,与当前在当前时间之前表示已经到期,之后则是未到期。为了方便计算,muduo重载了operator<主要是用来比较微秒大小。
到期的时间应该被清除去执行相应的回调,未到期的时间则应该有序的排列起来。
对于TimerQueue的数据结构,作者提出了几个方案。
1.传统线性表,查找复杂度为O(n)
2.二叉堆实现优先级队列,不过C++标准的make_heap()不能高效地完成删除操作。
最终,为了防止时间相同所导致的Key相同的情况,使用set和pair
typedef std::pair<Timestamp, Timer*>Entry; typedef std::set<Entry>TimerList; TimerList timers_;
Linux时间函数介绍
linux中用以获取当前时间的的函数有:
time(2) / time_t(秒)
ftime(3) / struct timeb(毫秒)
gettimeofday(2) / struct timeval(微秒)
clock_gettime(2) / struct timespec(微秒)
还有gmtime / localtime / timegm / mktime / strftime / struct tm等与当前时间无关的时间格式转换函数。
定时函数
sleep(3)
alarm(3)
usleep(3)
nanosleep(2)
clock_nanosleep(2)
gettimer(2) / settitimer(2)
timer_create(2) / timer_settime(2) / tiemr_gettime(2) / timer_delete(2)
timerfd_create(2) / timerfd_gettime(2) / timerfd_settime(2)
取舍如下:
1、计时只使用gettimeofday(2)来获取当前时间。
2、定时只使用timerfd_*系列函数来处理定时任务。
timerfd介绍
这节介绍muduo中定时器的实现。先看一个2.6内核新增的有关定时的系统调用,基于这几个系统调用可以实现基于文件描述符的定时器。即可是定时,使文件描述符在某一特定时间可读。
#include <sys/timerfd.h> 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);
1、timerfd_create用于创建一个定时器文件,函数返回值是一个文件句柄fd。
2、timerfd_settime用于设置新的超时时间,并开始计时。flag为0表示相对时间,为1表示绝对时间。new_value为这次设置的新时间,old_value为上次设置的时间。返回0表示设置成功。
3、timerfd_gettime用于获得定时器距离下次超时还剩下的时间。如果调用时定时器已经到期,并且该定时器处于循环模式(设置超时时间时struct itimerspec::it_interval不为0),那么调用此函数之后定时器重新开始计时。
TimerId介绍
TimerId非常简单,它被设计用来取消Timer的,它的结构很简单,只有一个Timer指针和其序列号。
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) // // This is a public header file, it must only include public header files. /*封装了timer类到构造和析构函数中,我的理解就是RAII的思想 * TimerId非常简单,它被设计用来取消Timer的,它的结构很简单,只有一个Timer指针和其序列号。*/ #ifndef MUDUO_NET_TIMERID_H #define MUDUO_NET_TIMERID_H #include <muduo/base/copyable.h> namespace muduo { namespace net { class Timer; /// /// 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_; }; } } #endif // MUDUO_NET_TIMERID_H
Timer
Timer封装了定时器的一些参数,例如超时回调函数、超时时间、定时器是否重复、重复间隔时间、定时器的序列号。其函数大都是设置这些参数,run()用来调用回调函数,restart()用来重启定时器(如果设置为重复)。其源码相对简单
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) // // This is an internal header file, you should not include this. /*计时器类*/ #ifndef MUDUO_NET_TIMER_H #define MUDUO_NET_TIMER_H #include <boost/noncopyable.hpp> #include <muduo/base/Atomic.h> #include <muduo/base/Timestamp.h> #include <muduo/net/Callbacks.h> namespace muduo { namespace net { /// /// Internal class for timer event. /// class Timer : boost::noncopyable { public: Timer(const TimerCallback &cb, Timestamp when, double interval) : callback_(cb),//回调函数 expiration_(when),//超时时间 interval_(interval),//如果重复,间隔时间 repeat_(interval > 0.0),//如果间隔大于0,就重复 sequence_(s_numCreated_.incrementAndGet()) {}//当前定时器的序列号 //调用回调函数. void run() const { 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_; // 超时时间间隔,如果是一次性定时器,该值为0 const bool repeat_; // 是否重复 const int64_t sequence_; // 定时器序号,不会重复 static AtomicInt64 s_numCreated_; // 定时器计数,当前已经创建的定时器数量 }; } } #endif // MUDUO_NET_TIMER_H
Timestamp
时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。
Timestamp类的参数有一个常量kMicroSecondsPerSecond表示每秒所对应的微秒数,成员变量microSecondsSinceEpoch_表示到1970-01-01 00:00:00 UTC的微秒数。成员函数包括swap()交换操作,toString()、toFormattedString()将时间转换为string类型或指定格式,valid()判断Timestamp是否有效,invalid()返回一个无效的Timestamp,now()返回当前时间的Timestamp,secondsSinceEpoch()/microSecondsSinceEpoch()返回到1970-01-01 00:00:00 UTC的秒数/微秒数。
Timestamp.h源码(带注释)
#ifndef MUDUO_BASE_TIMESTAMP_H #define MUDUO_BASE_TIMESTAMP_H /*时间戳函数,我的理解是创建一个microSecondsSinceEpoch_为0的结构体, *然后每次通过这个结构体创建子的microSecondsSinceEpoch_为当前时间的结构体,并且子结构体都在microSecondsSinceEpoch_为0的结构体 *中进行运算操作*/ #include <muduo/base/copyable.h> #include <muduo/base/Types.h> #include <boost/operators.hpp> namespace muduo { ///Timestamp /// Time stamp in UTC, in microseconds resolution. /// /// This class is immutable. /// It's recommended to pass it by value, since it's passed in register on x64. /// class Timestamp : public muduo::copyable, public boost::less_than_comparable<Timestamp>//继承这个类,对于<,>,<=,>=这些运算符号,只需要定义 //<号,其他的都可以自动帮你定义了 { public: /// /// Constucts an invalid Timestamp. /// Timestamp() : microSecondsSinceEpoch_(0) { } /// /// Constucts a Timestamp at specific time /// /// @param microSecondsSinceEpoch explicit Timestamp(int64_t microSecondsSinceEpoch); void swap(Timestamp &that)//将两个Timestamp类中的microSecondsSinceEpoch_变量交换 { std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_); } // default copy/assignment/dtor are Okay string toString() const; string toFormattedString() const; bool valid() const { return microSecondsSinceEpoch_ > 0; }//判断microSecondsSinceEpoch_是否有效,大于0就有效 // for internal usage. int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }//返回microSecondsSinceEpoch_ time_t secondsSinceEpoch() const//返回以秒为单位的microSecondsSinceEpoch_ { return static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); } /// /// Get time of now. /// static Timestamp now();//创建一个当前的时间的Timestamp结构体 static Timestamp invalid();//创建一个microSecondsSinceEpoch_=0的Timestamp结构体,都是静态函数,可以直接调用 static const int kMicroSecondsPerSecond = 1000 * 1000; private: int64_t microSecondsSinceEpoch_;//表示到1970-01-01 00:00:00 UTC的微秒数。 }; /*放在类外重载,但是普通数据类型可以使用原来的,遇到特定类型才会使用这个*/ inline bool operator<(Timestamp lhs, Timestamp rhs)//只需要定义<号,其他都自动定义,less_than_comparable模板作用 { return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch(); } inline bool operator==(Timestamp lhs, Timestamp rhs) { return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch(); } /// /// Gets time difference of two timestamps, result in seconds. /// /// @param high, low /// @return (high-low) in seconds /// @c double has 52-bit precision, enough for one-microseciond /// resolution for next 100 years. //计算两个Timestamp之间的差,以秒为单位 inline double timeDifference(Timestamp high, Timestamp low) { int64_t diff = high.microSecondsSinceEpoch() - low.microSecondsSinceEpoch(); return static_cast<double>(diff) / Timestamp::kMicroSecondsPerSecond; } /// /// Add @c seconds to given timestamp. /// /// @return timestamp+seconds as Timestamp /// //加时间 inline Timestamp addTime(Timestamp timestamp, double seconds) { int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond); return Timestamp(timestamp.microSecondsSinceEpoch() + delta); } } #endif // MUDUO_BASE_TIMESTAMP_H
Timestamp.cc源码(带注释)
#include <muduo/base/Timestamp.h> #include <sys/time.h> #include <stdio.h> #define __STDC_FORMAT_MACROS #include <inttypes.h> #undef __STDC_FORMAT_MACROS #include <boost/static_assert.hpp> using namespace muduo; BOOST_STATIC_ASSERT(sizeof(Timestamp) == sizeof(int64_t)); Timestamp::Timestamp(int64_t microseconds) : microSecondsSinceEpoch_(microseconds) { } string Timestamp::toString() const//转化成xxx.xxx秒的格式 { char buf[32] = {0}; int64_t seconds = microSecondsSinceEpoch_ / kMicroSecondsPerSecond; int64_t microseconds = microSecondsSinceEpoch_ % kMicroSecondsPerSecond; snprintf(buf, sizeof(buf) - 1, "%" PRId64 ".%06" PRId64 "", seconds, microseconds); return buf; } //将时间加上1900年1月1日之后转成"年月日 小时:分钟:秒"的格式 string Timestamp::toFormattedString() const { char buf[32] = {0}; time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); // 秒数 int microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond); struct tm tm_time;// 微秒数 // 把time_t结构中的信息转换成真实世界所使用的时间日期,存储在tm_time结构中 gmtime_r(&seconds, &tm_time);//将总秒数转换成————多少年多少月多少日多少小时多少分多少秒为单位 // 格式化输出时间戳 snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, microseconds);//总秒数加上1900年1月1日然后转换成固定格式 return buf; } Timestamp Timestamp::now() { struct timeval tv; gettimeofday(&tv, NULL);//得到的是系统从当前时间从1900年1月1日开始算起的总秒数 int64_t seconds = tv.tv_sec; return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec); //返回一个Timestamp结构体,相当于创建一个当前时间的Timestamp结构体 } Timestamp Timestamp::invalid() { return Timestamp(); }
TimerQueue
虽然TimerQueue中有Queue,但是其实现时基于Set的,而不是Queue。这样可以高效地插入、删除定时器,且找到当前已经超时的定时器。TimerQueue的public接口只有两个,添加和删除。
void addTimerInLoop(Timer* timer); void cancelInLoop(TimerId timerId);
定时器管理类,其中timer类就是TimerQueue需要管理的元素,而timerId就是一个简单的timer封装,避免销毁和创建操作
但是要注意的是timer并没有自己计时的功能,所以需要依靠timerfd这个系统函数统一计时timerfd是一个系统计时函数,当所设置的时间到了,会通过timerfd这个文件描述符进行提示通信,而其他计时函数可能是通过信号量,或者其他方式,但是都没有文件描述符好,并且也可以用timerfd监听,具体原因可以查看一下博客的网络库定时器实现
如何使用timerfd来为所有的计时器计时:timerfd每次都设置在计时器列表中到期时间最近的那个到期时间,这样timerfd到期以后,也就是最近的那个计时器到期,所以每次都是手动重置timerfd的计时时间,为最近的计时器到期时间
内部有channel,和timerfd关联。添加新的Timer后,在超时后,timerfd可读,会处理channel事件,之后调用Timer的回调函数;在timerfd的事件处理后,还有检查一遍超时定时器,如果其属性为重复还有再次添加到定时器集合中。
内部有两种类型的Set
typedef std::pair<Timestamp, Timer*> Entry; typedef std::set<Entry> TimerList; typedef std::pair<Timer*, int64_t> ActiveTimer; typedef std::set<ActiveTimer> ActiveTimerSet;
下面给出TimerQueue.h和TimerQueue.cc源码分析有详细的注释
TimerQueue.h
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) // // This is an internal header file, you should not include this. /* 定时器管理类,其中timer类就是TimerQueue需要管理的元素,而timerId就是一个简单的timer封装,避免销毁和创建操作 * 但是要注意的是timer并没有自己计时的功能,所以需要依靠timerfd这个系统函数统一计时 * timerfd是一个系统计时函数,当所设置的时间到了,会通过timerfd这个文件描述符进行提示通信,而其他计时函数可能是通过信号量,或者 * 其他方式,但是都没有文件描述符好,并且也可以用timerfd监听,具体原因可以查看一下博客的网络库定时器实现 * 如何使用timerfd来为所有的计时器计时:timerfd每次都设置在计时器列表中到期时间最近的那个到期时间,这样timerfd到期以后,也就是最近的那个计时器到期 * 所以每次都是手动重置timerfd的计时时间,为最近的计时器到期时间 * ?timestamp::now获得的时间是从1960年1月1日开始的,但是timerfd据说是从系统开机的那一刻开始的,那么在设置timefd时时间不统一怎么办 * 注意在timerfd设置延时的时候,使用的是相对时间,所以无所谓最终时间是多少,只要相对时间没问题就好了 * ?重置timerfd导致延时怎么办 * ?关于线程执行,究竟哪些函数靠IO线程来执行 * * */ #ifndef MUDUO_NET_TIMERQUEUE_H #define MUDUO_NET_TIMERQUEUE_H #include <set> #include <vector> #include <boost/noncopyable.hpp> #include <muduo/base/Mutex.h> #include <muduo/base/Timestamp.h> #include <muduo/net/Callbacks.h> #include <muduo/net/Channel.h> namespace muduo { namespace net { class EventLoop; class Timer; class TimerId; /// /// A best efforts timer queue. /// No guarantee that the callback will be on time. /// 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; //set中存储的是pair类型,那么默认先按照pair的第一个元素排序,如果相同,再按照第二个元素排序。 //所以这两种set都是存放定时器的列表,但是一个根据定时器的到时时间来存储, //一个根据定时器地址来存储,但是存储的定时器都是同一个,目的是为了区分同一到期时间的定时器??? // 以下成员函数只可能在其所属的I/O线程中调用,因而不必加锁。 // 服务器性能杀手之一是锁竞争,所以要尽可能少用锁 void addTimerInLoop(Timer *timer); void cancelInLoop(TimerId timerId); // called when timerfd alarms void handleRead();//timerfdChannel_的读函数 // 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_; // 所属EventLoop const int timerfd_; //过一段事件,就筛选一次,看看TimerList中有多少定时器到时间了,就处理一下,但是这样延迟很高,不太理解 Channel timerfdChannel_;//与timefd绑定 // Timer list sorted by expiration TimerList timers_; // timers_是按到期时间排序,也是存放未到时间的定时器 // for cancel() // timers_与activeTimers_保存的是相同的数据 // timers_是按到期时间排序,activeTimers_是按对象地址排序 ActiveTimerSet activeTimers_;//还未到时间的定时器,这里面存放的定时器是和timers_一样的,只是顺序不同 bool callingExpiredTimers_; /* atomic *///是否在处理过期定时器的标志 ActiveTimerSet cancelingTimers_; // 保存的是被取消的定时器 // 用这个列表的作用是,当出现一个循环的计时器被取消时,就要通过reset函数中对 //ActiveTimerSet列表来暂停对这个计时器的重置 }; } } #endif // MUDUO_NET_TIMERQUEUE_H
TimerQueue.cc
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) #define __STDC_LIMIT_MACROS #include <muduo/net/TimerQueue.h> #include <muduo/base/Logging.h> #include <muduo/net/EventLoop.h> #include <muduo/net/Timer.h> #include <muduo/net/TimerId.h> #include <boost/bind.hpp> #include <sys/timerfd.h> namespace muduo { namespace net { namespace detail { // 创建定时器 int createTimerfd() { int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);//CLOCK_MONOTONIC参数表明计时器的时间是从系统打开开始计时的 //CLOCK_MONOTONIC表示的是时间类型 if (timerfd < 0) { LOG_SYSFATAL << "Failed in timerfd_create"; } return timerfd; } // 计算超时时刻与当前时间的时间差 struct timespec howMuchTimeFromNow(Timestamp when) { int64_t microseconds = when.microSecondsSinceEpoch() - Timestamp::now().microSecondsSinceEpoch(); if (microseconds < 100) { microseconds = 100; } struct timespec ts; ts.tv_sec = static_cast<time_t>( microseconds / Timestamp::kMicroSecondsPerSecond); ts.tv_nsec = static_cast<long>( (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000); return ts; } // 清除定时器,避免一直触发//处理超时事件。超时后,timerfd变为可读 void readTimerfd(int timerfd, Timestamp now) { uint64_t howmany; ssize_t n = ::read(timerfd, &howmany, sizeof howmany); LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString(); if (n != sizeof howmany) { LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8"; } } // 重置定时器的超时时间(不是周期性的定时器,时间到expiration就结束了) // 在这里面itimerspec.it_interval都是设置的0,每次都是计时结束以后手动重新设置,然后再计时的。 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); if (ret) { LOG_SYSERR << "timerfd_settime()"; } } } } } using namespace muduo; using namespace muduo::net; using namespace muduo::net::detail; TimerQueue::TimerQueue(EventLoop *loop) : loop_(loop), timerfd_(createTimerfd()), timerfdChannel_(loop, timerfd_),//timerfd相关的channel timers_(), callingExpiredTimers_(false) { timerfdChannel_.setReadCallback( boost::bind(&TimerQueue::handleRead, this)); // we are always reading the timerfd, we disarm it with timerfd_settime. timerfdChannel_.enableReading();//设置关注读事件,并且加入epoll队列 } TimerQueue::~TimerQueue() { ::close(timerfd_); // do not remove channel, since we're in EventLoop::dtor(); for (TimerList::iterator it = timers_.begin(); it != timers_.end(); ++it) { delete it->second; } } TimerId TimerQueue::addTimer(const TimerCallback &cb, Timestamp when, double interval)//创建并增加Timer进队列中 { Timer *timer = new Timer(cb, when, interval); loop_->runInLoop( boost::bind(&TimerQueue::addTimerInLoop, this, timer)); //addTimerInLoop(timer); return TimerId(timer, timer->sequence()); } void TimerQueue::cancel(TimerId timerId)//取消 { loop_->runInLoop( boost::bind(&TimerQueue::cancelInLoop, this, timerId)); //cancelInLoop(timerId); } void TimerQueue::addTimerInLoop(Timer *timer) { loop_->assertInLoopThread(); // 插入一个定时器,有可能会使得最早到期的定时器发生改变 bool earliestChanged = insert(timer); if (earliestChanged) { // 重置timefd定时器的超时时刻(timerfd_settime) resetTimerfd(timerfd_, timer->expiration()); } } void TimerQueue::cancelInLoop(TimerId timerId)//取消的回调函数 //取消计时器,就是把该计时器从两个队列中删除, //现在有一种特殊情况,就是如果刚好在处理定时器的过程中,并且这个要取消的定时器就是在被处理的,并且是循环定时器,那么如果不加入cancelingTimers_列表 //就会出现,在重置时又把这个定时器重启了,但是这个定时器应该是要被取消的 { 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 please,如果用了unique_ptr,这里就不需要手动删除了 activeTimers_.erase(it); }//用activeTimers_列表来搜索,然后找到先删除timers_,再删除activeTimers_ else if (callingExpiredTimers_) //如果在未到时间的定时器中没有找到,并且线程正在处理过期的定时器,那么可能这个定时器正在被处理,就将这些定时器放到cancelingTimers_数组中 { // 已经到期,并且正在调用回调函数的定时器,为了在重置时,避免被重置,而是被忽略 cancelingTimers_.insert(timer); } assert(timers_.size() == activeTimers_.size()); } void TimerQueue::handleRead()//TimerChannel的回调函数,也就是当timefd定时器到时的时候,就会调用这个函数 { loop_->assertInLoopThread(); Timestamp now(Timestamp::now()); readTimerfd(timerfd_, now); // 清除该事件,避免一直触发 // 获取该时刻之前所有的定时器列表(即超时定时器列表) std::vector <Entry> expired = getExpired(now); callingExpiredTimers_ = true;//处理到期的定时器 cancelingTimers_.clear();//每次处理前,把要取消的定时器列表清空 // safe to callback outside critical section for (std::vector<Entry>::iterator it = expired.begin(); it != expired.end(); ++it) { // 这里回调定时器timer处理函数 it->second->run(); } callingExpiredTimers_ = false; // 不是一次性定时器,需要重启 reset(expired, now);//如果之前处理定时器回调函数时间较长,那么在这段时间中,已经有定时器到期了,轻则产生延迟,重则 } // rvo 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));//我理解是找了一个指针可以取到的最大数,为了避免和其他指针冲突, //因为这个指针没有什么意义,仅仅是为了构成一个Entry结构体,有意义的是第一个元素now // 返回第一个未到期的Timer的迭代器 // lower_bound的含义是返回第一个值>=sentry的元素的iterator // 即*end >= sentry,从而end->first > now TimerList::iterator end = timers_.lower_bound(sentry); assert(end == timers_.end() || now < end->first); // 将到期的定时器插入到expired中 std::copy(timers_.begin(), end, back_inserter(expired));//back_inserter是迭代器的一种操作,效果和expired.push_back()一样 // 从timers_中移除到期的定时器 timers_.erase(timers_.begin(), end); // 从activeTimers_中移除到期的定时器 for (std::vector<Entry>::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;//避免编译器出现变量n未使用的警告??? } assert(timers_.size() == activeTimers_.size()); return expired; } void TimerQueue::reset(const std::vector <Entry> &expired, Timestamp now)//重启两种定时器,一种是timefd,另外一种是定时器列表中需要重复的定时器 { Timestamp nextExpire; //重启定时器列表中过期的定时器,如果需要重复的话 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//不需要重复就删除这个定时器 { // 一次性定时器或者已被取消的定时器是不能重置的,因此删除该定时器 // FIXME move to a free list delete it->second; // FIXME: no delete please } } //重启timefd,设置的时间就是定时器列表中最快到期的时间 if (!timers_.empty()) { // 获取最早到期的定时器超时时间 nextExpire = timers_.begin()->second->expiration(); } if (nextExpire.valid()) { // 重置定时器的超时时刻(timerfd_settime) resetTimerfd(timerfd_, nextExpire); } } bool TimerQueue::insert(Timer *timer)//把定时器插入到timers_和activeTimers_队列中去 { loop_->assertInLoopThread(); assert(timers_.size() == activeTimers_.size()); // 最早到期时间是否改变 bool earliestChanged = false;//这个变量的意义是显示最早到期时间是否改变,通俗点说就是这个插入的定时器的位置在timers_的 //首位,也就是这个插入的定时器的到期时间是timers_中已经存储的定时器中最早的,那么这个标志位就会置true Timestamp when = timer->expiration();//超时时刻 TimerList::iterator it = timers_.begin(); // 如果timers_为空或者when小于timers_中的最早到期时间 if (it == timers_.end() || when < it->first) { earliestChanged = true;//表示定时器最早,所以置true } //要分别插入到两个set中 { // 插入到timers_中 std::pair<TimerList::iterator, bool> result = timers_.insert(Entry(when, 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;//返回最早到期的时间有没有改变 }