(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;//返回最早到期的時間有沒有改變 }