一般比较定时器的实现方式有几种
1、升序时间链表,然后tick轮询方式
2、使用时间轮的方式,主要是解决定时器过多,插入时间链表的时间会增长的情况.设定n个槽位,每个槽是一个时间链表
假设有60个槽,每个槽的tick是1s,超时的设定跟槽关系就是 槽位置ts = (cur+(timeout/tick)) % 60, rotation = (timeout/tick )/60
3、使用最小堆的方式(libevent的定时器)libevent是自己写的堆结构,muduo使用的set集合保存时间信息(也是堆结构),libevent借助epoll最后一个参数触发,moduo直接通过循环触发。
实现方式:
#pragma once
#include <functional>
#include <event2/event_compat.h>
#include <event2/event_struct.h>
#include <event2/event.h>
#include <map>
class LibEventTimer
{
public:
typedef std::function<void(int)> Callback;
struct TimerPar
{
TimerPar()
{
pEvent = NULL;
fIntervalTime = 0;
bLoop = false;
nTimerID = -1;
}
struct event* pEvent;
float fIntervalTime;
Callback callBack;
bool bLoop;
int nTimerID;
};
public:
LibEventTimer();
virtual~LibEventTimer();
int Run_EveryTimer(float fIntervalTime, Callback callBack);
int Run_AfterTimer(float fIntervalTime, Callback callBack);
void CancelTimer(int nTimerID);
void CancelAllTimer();
static bool Init();
static void UnInit();
static bool HeartBeat();
private:
int CreateTimer(float fIntervalTime, Callback callBack, bool bLoop);
private:
static struct event_base* base;
int m_nBaseTimerID;
typedef std::map<int, TimerPar> TimerParMap;
TimerParMap m_TimerParsMap;
};
#include "Timer.h"
struct event_base* DK_Timer::base = NULL;
LibEventTimer::LibEventTimer()
{
m_nBaseTimerID = 0;
m_TimerParsMap.clear();
}
LibEventTimer::~LibEventTimer()
{
TimerParMap::iterator it = m_TimerParsMap.begin();
for (; it != m_TimerParsMap.end();)
{
TimerPar& timerPar = it->second;
if (timerPar.pEvent)
{
evtimer_del(timerPar.pEvent);
delete timerPar.pEvent;
it = m_TimerParsMap.erase(it);
}
else
++it;
}
m_TimerParsMap.clear();
m_nBaseTimerID = 0;
}
bool LibEventTimer::Init()
{
base = event_base_new();
return true;
}
void LibEventTimer::UnInit()
{
if (base)
{
event_base_free(base);
base = NULL;
}
}
void OnTimer(int sock, short event, void *arg)
{
LibEventTimer* pThis = (LibEventTimer*)arg;
LibEventTimer::TimerPar* pPar = (LibEventTimer::TimerPar*)arg;
if (pPar->bLoop)
{
float fIntervalTime = pPar->fIntervalTime;
time_t nScend = (time_t)fIntervalTime;
time_t nUsec = (fIntervalTime - nScend) * 1000000;
struct timeval tv;
tv.tv_sec = nScend;
tv.tv_usec = nUsec;
evtimer_add(pPar->pEvent, &tv);
}
if (pPar->callBack)
pPar->callBack(pPar->nTimerID);
}
int LibEventTimer::Run_EveryTimer(float fIntervalTime, Callback callBack)
{
return CreateTimer(fIntervalTime, callBack, true);
}
int LibEventTimer::Run_AfterTimer(float fIntervalTime, Callback callBack)
{
return CreateTimer(fIntervalTime, callBack, false);
}
int LibEventTimer::CreateTimer(float fIntervalTime, Callback callBack, bool bLoop)
{
time_t nScend = (time_t)fIntervalTime;
time_t nUsec = (fIntervalTime - nScend) * 1000000;
int nNewTimerID = m_nBaseTimerID++;
TimerPar timerParm;
timerParm.fIntervalTime = fIntervalTime;
timerParm.callBack = callBack;
timerParm.bLoop = bLoop;
timerParm.nTimerID = nNewTimerID;
struct timeval tv;
tv.tv_sec = nScend;
tv.tv_usec = nUsec;
struct event* timer_ev = evtimer_new(base, OnTimer, &m_TimerParsMap[nNewTimerID]);
timerParm.pEvent = timer_ev;
m_TimerParsMap[nNewTimerID] = timerParm;
evtimer_add(timer_ev, &tv);
return nNewTimerID;
}
void LibEventTimer::CancelTimer(int nTimerID)
{
TimerParMap::iterator it = m_TimerParsMap.find(nTimerID);
if (it == m_TimerParsMap.end())
{
return;
}
if (it->second.pEvent)
{
evtimer_del(it->second.pEvent);
delete it->second.pEvent;
it->second.pEvent = NULL;
}
m_TimerParsMap.erase(it);
}
void LibEventTimer::CancelAllTimer()
{
auto it = m_TimerParsMap.begin();
while (it != m_TimerParsMap.end())
{
int nTimerID = it->first;
CancelTimer(nTimerID);
it = m_TimerParsMap.begin();
}
}
bool LibEventTimer::HeartBeat()
{
event_base_loop(base, EVLOOP_NONBLOCK);
return true;
}
4、使用set这样的结构保存定时器信息(muduo定时器的实现方式)
这是别人给出的muduo定时器的使用方式,非常简单
https://blog.csdn.net/YoungSusie/article/details/93708907
(3,4 的触发是使用的epoll的超时timeout来触发的)