基于时间轮的定时器
原理
图片是一个单层时间轮,当指针走到某一格上,就获取那一格上挂的任务将其执行。
当时如果时间跨度大的时候,格子数明显不够,那么就可以做成多级时间轮。
其实就是当低层的时间轮走了一圈,将它高一层的时间轮走一格,并且将挂在高层时间轮上的任务分配下来。
实现
文件#include"TimeWheel.h"
#include<functional>
#include<list>
#include<thread>
#include<mutex>
struct TimeStamp //时间戳
{
int ms;
int s;
int min;
bool operator==(TimeStamp timeStamp)
{
return timeStamp.ms==ms&&timeStamp.s==s&&timeStamp.min==min;
}
};
struct Event //挂在时间槽上的事件
{
std::function<void(void)> call_back;
TimeStamp tri_time; //事件触发的时间
int dur; //多久触发
};
class TimeWheel //时间轮
{
public:
TimeWheel();
~TimeWheel();
void Start(); //启动
int AddTimer(int space,std::function<void(void)>& call_back); //添加定期器
private:
void DoLoop();//主循环
void InsertTimer(Event &event); //插入事件到时间槽
int getMS(const TimeStamp &timeStamp);
void solvingEvents(std::list<Event>l); //解决时间槽上的事件
void getNextTime(TimeStamp &nowTimeStamp,int dur); //得到下个时间
private:
std::list<Event> *callBackList = nullptr; //当做时间槽上,每一个槽都是一个链表
std::mutex _mutex; //互斥量
TimeStamp timeStamp; //事件轮的时间
int slot_ms; //毫秒的时间槽数量
int slot_s; //秒的时间槽数量
int slot_min; //分的时间槽数量
int step; //最小的间隔时间
};
文件#include"TimeWheel.cpp"
#include"TimeWheel.h"
#include<iostream>
using namespace std;
TimeWheel::TimeWheel()
{
cout<<"TimeWheel()"<<endl;
timeStamp.ms = timeStamp.s = timeStamp.min = 0;
slot_ms=10;
slot_s=60;
slot_min=60;
step = 100;
callBackList = new list<Event>[slot_ms+slot_s+slot_min];
}
TimeWheel::~TimeWheel()
{
delete[] callBackList;
}
void TimeWheel::Start() //开启一个线程
{
cout<<"Start()"<<endl;
std::thread myThread([&]{
this->DoLoop();
});
myThread.detach();
}
void TimeWheel::DoLoop() //主循环
{
cout<<"start"<<endl;
while(true)
{
this_thread::sleep_for(chrono::milliseconds(step)); //让线程休息step毫秒的时间
unique_lock<std::mutex> lock(_mutex);
cout<<"time:"<<timeStamp.min <<" "<<timeStamp.s <<" "<<timeStamp.ms<<endl;
TimeStamp per = timeStamp; //per是原来的时间
getNextTime(timeStamp,step); //timeStamp向后走一步
if(per.min!=timeStamp.min) //分针有进位
{
//cout<<"(check min :" << slot_ms+slot_s+timeStamp.min <<")"<<endl;
list<Event>& l = callBackList[slot_ms+slot_s+timeStamp.min];
solvingEvents(l);
l.clear();
}
else if(per.s!=timeStamp.s) //秒针有进位
{
//cout<<"(check s :" << slot_ms+timeStamp.s <<")"<<endl;
list<Event>& l = callBackList[slot_ms+timeStamp.s];
solvingEvents(l);
l.clear();
}
else if(per.ms!=timeStamp.ms) //毫秒有进位
{
//cout<<"(check ms :" << timeStamp.ms <<")"<<endl;
list<Event>& l = callBackList[timeStamp.ms];
solvingEvents(l);
l.clear();
}
lock.unlock();
}
}
int TimeWheel::getMS(const TimeStamp &timeStamp) //得到时间戳timeStamp一共多少ms
{
return step * timeStamp.ms + timeStamp.s * 1000 + timeStamp.min * 60 * 1000;
}
void TimeWheel::solvingEvents(list<Event>l)
{
for (auto item = l.begin(); item != l.end(); item++)
{
if(timeStamp == item->tri_time) //触发时间到了
{
item->call_back(); //执行函数
//如果需要发生多次加入下面两行
getNextTime(item->tri_time,item->dur);
InsertTimer(*item);
}
else
{
InsertTimer(*item);
}
}
}
void TimeWheel::getNextTime(TimeStamp &nowTimeStamp,int dur)//获得下一个时间
{
int next_ms = getMS(nowTimeStamp)+dur;
nowTimeStamp.min = next_ms/1000/60%slot_min;
nowTimeStamp.s = (next_ms%(1000*60))/1000;
nowTimeStamp.ms = (next_ms%1000)/step;
}
//添加定时器
int TimeWheel::AddTimer(int space,function<void(void)>& call_back)
{
if(space<step)return -1; //发生事件小于最小间隔
if(space%step!=0)return -1;
unique_lock<std::mutex> lock(_mutex);
Event event;
event.call_back = call_back;
event.tri_time.ms = timeStamp.ms;
event.tri_time.s = timeStamp.s;
event.tri_time.min = timeStamp.min;
event.dur = space;
getNextTime(event.tri_time,event.dur);
//cout<<"add a "<<space<<" clock"<<endl;
cout<<event.tri_time.min<<":"<<event.tri_time.s<<":"<<event.tri_time.ms<<endl;
InsertTimer(event);
lock.unlock();
return 0;
}
//先时间轮内插入事件
void TimeWheel::InsertTimer(Event &event)
{
if(event.tri_time.min != timeStamp.min) //分钟与现在不同的分钟就插入分的槽
callBackList[slot_ms + slot_s + event.tri_time.min].push_back(event);
else if(event.tri_time.s != timeStamp.s)
callBackList[slot_ms + event.tri_time.s].push_back(event);
else if(event.tri_time.ms != timeStamp.ms)
callBackList[event.tri_time.ms].push_back(event);
}
测试文件Main.cpp
#include<cstdio>
#include<cstring>
#include"TimeWheel.h"
#include<iostream>
using namespace std;
void fun100()
{
cout << "------------fun 100--------------" << endl;
}
void fun200()
{
cout << "------------fun 200--------------" << endl;
}
void fun500()
{
cout << "------------fun 500--------------" << endl;
}
void fun1500()
{
cout << "------------fun 1500-------------" << endl;
}
int main()
{
function<void(void)> f100 = std::bind(&fun100);
function<void(void)> f200 = std::bind(&fun200);
function<void(void)> f500 = std::bind(&fun500);
function<void(void)> f1500 = std::bind(&fun1500);
TimeWheel timeWheel;
timeWheel.Start();
//加入定时器
timeWheel.AddTimer(100,f100);
timeWheel.AddTimer(200,f200);
timeWheel.AddTimer(500,f500);
timeWheel.AddTimer(1500,f1500);
while(true){}
return 0;
}