定时器作为常用的组件,通常的实现方式有几种:链表,最小堆,时间轮,等等。
1 链表
性能比较弱,适用于简单的场景,查询、插入、删除效率都不高
2 最小堆
性能比较高,适用于定时器需求较多的场景
3 时间轮
性能比较高,适用于定时器需求很大的场景
在网上查了一些资料,真正可以直接用的代码并不多,不可以直接运行,就不能给读者以更加感性的认识,也就不愿意更多的去分析代码和学习。所以,以 https://www.cnblogs.com/junye/p/5836552.html 为例,将它的最小堆实现,做了一些修改,可以接在gcc 4.8.2以上的环境编译通过并运行。将代码保存为 a.cc,编译命令为
g++ -g -o a a.cc -std=c++11
具体的代码如下:
#include <iostream>
#include <chrono>
#include <thread>
#include <vector>
#include <sys/time.h>
// global declaration
typedef void (*Fun)(void);
class Timer;
class TimerManager;
// header file for Timer
class Timer
{
public:
enum TimerType { ONCE, CIRCLE };
Timer(TimerManager& manager);
~Timer();
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_;
Fun timerFun_;
unsigned interval_;
unsigned long long expires_;
int heapIndex_;
};
// header file for TimerManager
class TimerManager
{
public:
static unsigned long long GetCurrentMillisecs();
void DetectTimers();
private:
friend class Timer;
void AddTimer(Timer* timer);
void RemoveTimer(Timer* timer);
void UpHeap(int index);
void DownHeap(int index);
void SwapHeap(int, int index2);
private:
struct HeapEntry
{
unsigned long long time;
Timer* timer;
};
std::vector<HeapEntry> heap_;
};
// implemetation of Timer
Timer::Timer(TimerManager& manager)
: manager_(manager)
, heapIndex_(-1)
{
// to-do
}
Timer::~Timer()
{
Stop();
}
inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{
Stop();
interval_ = interval;
timerFun_ = fun;
timerType_ = timeType;
this->expires_ = this->interval_ + TimerManager::GetCurrentMillisecs();
manager_.AddTimer(this);
}
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_();
}
// implemetation of 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)
{
int 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();
int 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(int index)
{
int parent = index >> 1;
while (index > 0 && heap_[index].time < heap_[parent].time)
{
SwapHeap(index, parent);
index = parent;
parent = index >> 1;
}
}
void TimerManager::DownHeap(int index)
{
int child = (index << 1) + 1;
while (child < heap_.size())
{
int 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 << 1) + 1;
}
}
void TimerManager::SwapHeap(int index1, int 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()
{
timeval tv;
::gettimeofday(&tv, 0);
unsigned long long ret = tv.tv_sec;
return ret * 1000 + tv.tv_usec / 1000;
}
// test code
void TimerHandler_1()
{
std::cout << "TimerHandler_1" << std::endl;
}
void TimerHandler_2()
{
std::cout << "TimerHandler_2" << std::endl;
}
void TimerHandler_3()
{
std::cout << "TimerHandler_3" << std::endl;
}
void TimerHandler_4()
{
std::cout << "TimerHandler_4" << std::endl;
}
int main()
{
TimerManager tm;
Timer t1(tm);
t1.Start(&TimerHandler_1, 1000);
Timer t2(tm);
t2.Start(&TimerHandler_2, 500);
Timer t3(tm);
t3.Start(&TimerHandler_3, 1500);
Timer t4(tm);
t4.Start(&TimerHandler_4, 100);
while (true)
{
tm.DetectTimers();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}