基于时间轮的定时器

基于时间轮的定时器

原理

此处输入图片的描述
图片是一个单层时间轮,当指针走到某一格上,就获取那一格上挂的任务将其执行。
当时如果时间跨度大的时候,格子数明显不够,那么就可以做成多级时间轮。
其实就是当低层的时间轮走了一圈,将它高一层的时间轮走一格,并且将挂在高层时间轮上的任务分配下来。

实现

文件#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;
}

实现效果

此处输入图片的描述

posted @ 2019-04-13 23:03  xcantaloupe  阅读(1137)  评论(0编辑  收藏  举报