寫這篇文章前搜了下網上相似的文章,有不少,因此筆者的這篇文章就不對定時器的常見實現方法加以說明,也不進行性能比較,直接上代碼。linux
基於multimap實現的比較簡單,這裏略過。ios
前導程序員
對於大多數的服務器程序,其定時器通常支持單線程就夠了,通常使用方法見下面代碼。若是須要多線程怎麼辦,筆者通常用一個簡單的辦法:多線程的業務線程中不包含定時器管理器,單獨啓一個線程用來管理全部定時器,當時間觸發時,向業務線程投遞定時器消息便可。服務器
#include <iostream> #include <thread> #include <chrono> #include "Timer.h" void TimerHandler() { std::cout << "TimerHandler" << std::endl; } int main() { TimerManager tm; Timer t(tm); t.Start(&TimerHandler, 1000); while (true) { tm.DetectTimers(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::cin.get(); return 0; }
1 最小堆實現多線程
#include <vector> #include <boost/function.hpp> class TimerManager; class Timer { public: enum TimerType { ONCE, CIRCLE }; Timer(TimerManager& manager); ~Timer(); template<typename Fun> void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE); void Stop(); private: void OnTimer(unsigned long long now); private: friend class TimerManager; TimerManager& manager_; TimerType timerType_; boost::function<void(void)> timerFun_; unsigned interval_; unsigned long long expires_; size_t heapIndex_; }; class TimerManager { public: static unsigned long long GetCurrentMillisecs(); void DetectTimers(); private: friend class Timer; void AddTimer(Timer* timer); void RemoveTimer(Timer* timer); void UpHeap(size_t index); void DownHeap(size_t index); void SwapHeap(size_t, size_t index2); private: struct HeapEntry { unsigned long long time; Timer* timer; }; std::vector<HeapEntry> heap_; }; template<typename Fun> inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType) { Stop(); interval_ = interval; timerFun_ = fun; timerType_ = timeType; timer->expires_ = timer->interval_ + TimerManager::GetCurrentMillisecs(); manager_.AddTimer(this); } // cpp file ////////////////////////////////////////////////////////////////// #define _CRT_SECURE_NO_WARNINGS #include "config.h" #include "timer.h" #ifdef _MSC_VER # include <sys/timeb.h> #else # include <sys/time.h> #endif ////////////////////////////////////////////////////////////////////////// // Timer Timer::Timer(TimerManager& manager) : manager_(manager) , heapIndex_(-1) { } Timer::~Timer() { Stop(); } void Timer::Stop() { if (heapIndex_ != -1) { manager_.RemoveTimer(this); heapIndex_ = -1; } } void Timer::OnTimer(unsigned long long now) { if (timerType_ == Timer::CIRCLE) { expires_ = interval_ + now; manager_.AddTimer(this); } else { heapIndex_ = -1; } timerFun_(); } ////////////////////////////////////////////////////////////////////////// // TimerManager void TimerManager::AddTimer(Timer* timer) { timer->heapIndex_ = heap_.size(); HeapEntry entry = { timer->expires_, timer }; heap_.push_back(entry); UpHeap(heap_.size() - 1); } void TimerManager::RemoveTimer(Timer* timer) { size_t index = timer->heapIndex_; if (!heap_.empty() && index < heap_.size()) { if (index == heap_.size() - 1) { heap_.pop_back(); } else { SwapHeap(index, heap_.size() - 1); heap_.pop_back(); size_t parent = (index - 1) / 2; if (index > 0 && heap_[index].time < heap_[parent].time) UpHeap(index); else DownHeap(index); } } } void TimerManager::DetectTimers() { unsigned long long now = GetCurrentMillisecs(); while (!heap_.empty() && heap_[0].time <= now) { Timer* timer = heap_[0].timer; RemoveTimer(timer); timer->OnTimer(now); } } void TimerManager::UpHeap(size_t index) { size_t parent = (index - 1) / 2; while (index > 0 && heap_[index].time < heap_[parent].time) { SwapHeap(index, parent); index = parent; parent = (index - 1) / 2; } } void TimerManager::DownHeap(size_t index) { size_t child = index * 2 + 1; while (child < heap_.size()) { size_t minChild = (child + 1 == heap_.size() || heap_[child].time < heap_[child + 1].time) ? child : child + 1; if (heap_[index].time < heap_[minChild].time) break; SwapHeap(index, minChild); index = minChild; child = index * 2 + 1; } } void TimerManager::SwapHeap(size_t index1, size_t index2) { HeapEntry tmp = heap_[index1]; heap_[index1] = heap_[index2]; heap_[index2] = tmp; heap_[index1].timer->heapIndex_ = index1; heap_[index2].timer->heapIndex_ = index2; } unsigned long long TimerManager::GetCurrentMillisecs() { #ifdef _MSC_VER _timeb timebuffer; _ftime(&timebuffer); unsigned long long ret = timebuffer.time; ret = ret * 1000 + timebuffer.millitm; return ret; #else timeval tv; ::gettimeofday(&tv, 0); unsigned long long ret = tv.tv_sec; return ret * 1000 + tv.tv_usec / 1000; #endif }
2 時間輪實現性能
// header file ////////////////////////////////// #pragma once #include <list> #include <vector> #include <boost/function.hpp> class TimerManager; class Timer { public: enum TimerType {ONCE, CIRCLE}; Timer(TimerManager& manager); ~Timer(); template<typename Fun> void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE); void Stop(); private: void OnTimer(unsigned long long now); private: friend class TimerManager; TimerManager& manager_; TimerType timerType_; boost::function<void(void)> timerFun_; unsigned interval_; unsigned long long expires_; int vecIndex_; std::list<Timer*>::iterator itr_; }; class TimerManager { public: TimerManager(); static unsigned long long GetCurrentMillisecs(); void DetectTimers(); private: friend class Timer; void AddTimer(Timer* timer); void RemoveTimer(Timer* timer); int Cascade(int offset, int index); private: typedef std::list<Timer*> TimeList; std::vector<TimeList> tvec_; unsigned long long checkTime_; }; template<typename Fun> inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType) { Stop(); interval_ = interval; timerFun_ = fun; timerType_ = timeType; expires_ = interval_ + TimerManager::GetCurrentMillisecs(); manager_.AddTimer(this); } // cpp file ////////////////////////////////////////////////// #define _CRT_SECURE_NO_WARNINGS #include "config.h" #include "timer2.h" #ifdef _MSC_VER # include <sys/timeb.h> #else # include <sys/time.h> #endif #define TVN_BITS 6 #define TVR_BITS 8 #define TVN_SIZE (1 << TVN_BITS) #define TVR_SIZE (1 << TVR_BITS) #define TVN_MASK (TVN_SIZE - 1) #define TVR_MASK (TVR_SIZE - 1) #define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE) #define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK) ////////////////////////////////////////////////////////////////////////// // Timer Timer::Timer(TimerManager& manager) : manager_(manager) , vecIndex_(-1) { } Timer::~Timer() { Stop(); } void Timer::Stop() { if (vecIndex_ != -1) { manager_.RemoveTimer(this); vecIndex_ = -1; } } void Timer::OnTimer(unsigned long long now) { if (timerType_ == Timer::CIRCLE) { expires_ = interval_ + now; manager_.AddTimer(this); } else { vecIndex_ = -1; } timerFun_(); } ////////////////////////////////////////////////////////////////////////// // TimerManager TimerManager::TimerManager() { tvec_.resize(TVR_SIZE + 4 * TVN_SIZE); checkTime_ = GetCurrentMillisecs(); } void TimerManager::AddTimer(Timer* timer) { unsigned long long expires = timer->expires_; unsigned long long idx = expires - checkTime_; if (idx < TVR_SIZE) { timer->vecIndex_ = expires & TVR_MASK; } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { timer->vecIndex_ = OFFSET(0) + INDEX(expires, 0); } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { timer->vecIndex_ = OFFSET(1) + INDEX(expires, 1); } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { timer->vecIndex_ = OFFSET(2) + INDEX(expires, 2); } else if ((long long) idx < 0) { timer->vecIndex_ = checkTime_ & TVR_MASK; } else { if (idx > 0xffffffffUL) { idx = 0xffffffffUL; expires = idx + checkTime_; } timer->vecIndex_ = OFFSET(3) + INDEX(expires, 3); } TimeList& tlist = tvec_[timer->vecIndex_]; tlist.push_back(timer); timer->itr_ = tlist.end(); --timer->itr_; } void TimerManager::RemoveTimer(Timer* timer) { TimeList& tlist = tvec_[timer->vecIndex_]; tlist.erase(timer->itr_); } void TimerManager::DetectTimers() { unsigned long long now = GetCurrentMillisecs(); while (checkTime_ <= now) { int index = checkTime_ & TVR_MASK; if (!index && !Cascade(OFFSET(0), INDEX(checkTime_, 0)) && !Cascade(OFFSET(1), INDEX(checkTime_, 1)) && !Cascade(OFFSET(2), INDEX(checkTime_, 2))) { Cascade(OFFSET(3), INDEX(checkTime_, 3)); } ++checkTime_; TimeList& tlist = tvec_[index]; TimeList temp; temp.splice(temp.end(), tlist); for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr) { (*itr)->OnTimer(now); } } } int TimerManager::Cascade(int offset, int index) { TimeList& tlist = tvec_[offset + index]; TimeList temp; temp.splice(temp.end(), tlist); for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr) { AddTimer(*itr); } return index; } unsigned long long TimerManager::GetCurrentMillisecs() { #ifdef _MSC_VER _timeb timebuffer; _ftime(&timebuffer); unsigned long long ret = timebuffer.time; ret = ret * 1000 + timebuffer.millitm; return ret; #else timeval tv; ::gettimeofday(&tv, 0); unsigned long long ret = tv.tv_sec; return ret * 1000 + tv.tv_usec / 1000; #endif }
結束語this
在曾經的不少項目中,定時器的實現都是使用map,也許效率不是過高,卻歷來沒有成爲性能的瓶頸。可是程序員一般是追求完美的,既然有更好解決方案,且其實現又不那麼複雜,那就徹底能夠去嘗試。spa
申明線程
筆者自稱歷來不創造代碼,只是代碼的搬運工,例如上面最小堆是從boost中搬過來的,時間輪是從linux內核定時器中搬過來的,既然如此若是您願意,這裏代碼也能夠隨意使用(筆者其它博客裏的代碼也同樣),不須要筆者知曉或贊成,若是您發現有什麼bug或建議也請聯繫我。code