zoukankan      html  css  js  c++  java
  • 基于时间轮的定时器

    基于时间轮的定时器

    原理

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

    实现

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

    实现效果

    此处输入图片的描述

  • 相关阅读:
    QtAV编译
    git 恢复到刚刚clone下来的状态
    select2 清除选中项解决办法
    mysql: 查看某库表大小
    C# Linq 交集、并集、差集、去重
    mvc ajax访问后台时session过期无法跳转到Login页面问题解决
    Asp.net:上传文件超过了最大请求长度
    Firebug 没死,活在 Firefox DevTools 中
    vs2015 加载项目的时启动:无法启动 IIS Express Web 服务器
    Visual Studio安装SVN插件
  • 原文地址:https://www.cnblogs.com/xcantaloupe/p/10703405.html
Copyright © 2011-2022 走看看