分级时间轮c++简单实现

 

在实际工作中我们不能为每一个定时任务创建一个定时器,因为定时器也是一种珍贵的资源,在linux中可以使用alarm信号,timerfd,epoll_wait,select都可以来实现定时器。其中epoll_wait和select提供了延时作用,alarm采用异步的信号,timerfd系列需要在select/epoll中注册定时器事件EPOLLIN,并在事件发生后uint64_t x; read(timerfd,&x,sizeof(x));否则select和epoll的水平触发模式下会不断的提醒定时器事件。

时间轮算法可用来管理大量的定时事件,根据设置的定时时间与当前时间轮所在的位置(表盘上指针所指的位置),计算出这个定时事件应该被放入哪个桶中(时间轮上有多个tick,每个tick代表一个桶,桶可以是一个list,装着很多事件)。

定时溢出,时间轮算法还存在一个问题,就是它算表示的时间是用范围的,比如一个时间轮有60个tick,每个tick为1秒,那么如果想要定时70秒的定时事件将无法加入到时间轮中,当然可以增加tick的数量,比如将它增大的1000个,但是这样也只能表示1000秒范围内的定时任务,而且1000个tick也需要占用很多内存空间。

分级时间轮,将时间轮分级,就像钟表上的时分秒一样,当秒针到60的时候分钟才变动一下,当分钟到60时时针才变动一下。这样可增大表示范围,而且并不需要太多tick空间。当然每个tick的时长,tick的数量都是可以自定义的,在添加定时事件时,计算出定时任务触发时各个分级时间轮应在的位置(就像定时100秒,然后根据当前时间 + 100再转换成时分秒的位置),定时任务只有在时针对应时才去考虑分针,在分针对应时才考虑秒针,当秒针对应时说明定时时间已到,执行任务。

参考文章(里面有图,我就不画了):https://blog.csdn.net/xinzhongtianxia/article/details/86221241

下面代码:(我利用epoll_wait延时1秒,制作时分秒三个时间轮)

MyTimeWheel.h

#pragma once
/*
分层时间轮:时分秒
*/
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <list>
#include <functional>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
enum TIME{SECOND = 1,MINUTE = 60,HOUR = 3600};
typedef function<void()> TIMER_CALLBACK;
struct WheelPos {
	int hour;
	int minute;
	int second;
	WheelPos(int h,int m,int s) :hour(h), minute(m), second(s) {}
};
class MyTimerWheel;
struct timer_item {
private:
	bool	valid;
	WheelPos wheelPos;
	int		seconds;	//秒
	TIMER_CALLBACK func;
	bool	is_round;
public:
	friend class MyTimerWheel;
	timer_item(WheelPos pos,int tmout, bool isRound,TIMER_CALLBACK callFunc = nullptr):
		valid(true), wheelPos(pos),
		seconds(tmout),func(callFunc), is_round(isRound){
	
	}
};


class MyTimerWheel
{
public:
	MyTimerWheel();
	~MyTimerWheel();
public:
	//失败返回-1
	shared_ptr<timer_item>	addTimer(int seconds,bool is_round = false, TIMER_CALLBACK func = nullptr);
	void			delTimer(shared_ptr<timer_item> handle);
private:
	static void		thread_proc(void* pvoid);
	WheelPos		getWheelPos(int seconds);
private:
	enum {HOUR_TICK = 24,MINUTE_TICK = 60,SECOND_TICK = 60};
	unordered_map<int, list<shared_ptr<timer_item>>> hourWheel;
	unordered_map<int, list<shared_ptr<timer_item>>> minuteWheel;
	unordered_map<int, list<shared_ptr<timer_item>>> secondWheel;
	volatile int hourCurPos,minuteCurPos,secondCurPos;
	int epollFd;
	thread*	t;
	bool shutdown;
	mutex mutex_;
};

MyTimeWheel.cpp 

#include "MyTimerWheel.h"
#include <sys/epoll.h>
#include <unistd.h>

MyTimerWheel::MyTimerWheel():hourCurPos(0), minuteCurPos(0), secondCurPos(0),
 epollFd(0), shutdown(false){
	epollFd = epoll_create(5);
	t = new thread(bind(thread_proc, (void*)this));
}

MyTimerWheel::~MyTimerWheel()
{
	shutdown = true;
	t->join();
	delete t;
	close(epollFd);
}

shared_ptr<timer_item>	MyTimerWheel::addTimer(int seconds, bool is_round, TIMER_CALLBACK func) {
	if (seconds <= 0) {
		return nullptr;
	}
	auto wheelPos = getWheelPos(seconds);
	shared_ptr<timer_item> timerptr(new timer_item(wheelPos, seconds, is_round, func));
	lock_guard<mutex> lock(mutex_);
	if (wheelPos.hour >= 0) {
		hourWheel[wheelPos.hour].push_back(timerptr);
	}
	else if (wheelPos.minute >= 0) {
		minuteWheel[wheelPos.minute].push_back(timerptr);
	}
	else if(wheelPos.second >= 0) {
		secondWheel[wheelPos.second].push_back(timerptr);
	}
	return timerptr;
}

void			MyTimerWheel::delTimer(shared_ptr<timer_item> handle) {
	auto wheelPos = handle->wheelPos;
	lock_guard<mutex> lock(mutex_);
	if (wheelPos.hour >= 0) {
		hourWheel[wheelPos.hour].remove(handle);
	}
	else if (wheelPos.minute >= 0) {
		minuteWheel[wheelPos.minute].remove(handle);
	}
	else if (wheelPos.second >= 0) {
		secondWheel[wheelPos.second].remove(handle);
	}
}


WheelPos		MyTimerWheel::getWheelPos(int seconds) {
	int h = -1, m = -1, s = -1;
	if (seconds >= HOUR * 1) {
		int tmp = seconds / (HOUR * 1);
		h = (tmp + hourCurPos) % HOUR_TICK;
		seconds -= (tmp * HOUR * 1);
	}
	if (seconds >= MINUTE * 1) {
		int tmp = seconds / (MINUTE * 1);
		m = (tmp + minuteCurPos) % MINUTE_TICK;
		seconds -= (tmp * MINUTE * 1);
	}
	s = (secondCurPos + seconds) % SECOND_TICK;
	
	return WheelPos(h, m, s);
}

void		MyTimerWheel::thread_proc(void* pvoid) {
	MyTimerWheel* mtw = (MyTimerWheel*)pvoid;
	struct epoll_event events[5];
	while (!mtw->shutdown) {
		int ret = epoll_wait(mtw->epollFd, events, 1024, 1000);
		if (ret == 0) {
			lock_guard<mutex> lock(mtw->mutex_);
			++mtw->secondCurPos;
			if (mtw->secondCurPos == SECOND_TICK) {
				mtw->secondCurPos = 0;
				++mtw->minuteCurPos;
				for (auto x : mtw->minuteWheel[(int)mtw->minuteCurPos]) {
					if (x->valid) {
						mtw->secondWheel[x->wheelPos.second].push_back(x);
					}
				}
				mtw->minuteWheel[(int)mtw->minuteCurPos].clear();
			}
			if (mtw->minuteCurPos == MINUTE_TICK) {
				mtw->minuteCurPos = 0;
				++mtw->hourCurPos;
				for (auto x : mtw->hourWheel[(int)mtw->hourCurPos]) {
					if (x->valid) {
						mtw->minuteWheel[x->wheelPos.minute].push_back(x);
					}
				}
				mtw->hourWheel[(int)mtw->hourCurPos].clear();
			}
			if (mtw->hourCurPos == HOUR_TICK) {
				mtw->hourCurPos = 0;
			}
			for (auto x : mtw->secondWheel[(int)mtw->secondCurPos]) {
				if (x->valid) {
					x->func();
				}
				if (!x->is_round) {
					x->valid = false;
				}
				else {
					auto wheelPos = mtw->getWheelPos(x->seconds);
					x->wheelPos = wheelPos;
					if (wheelPos.hour >= 0) {
						mtw->hourWheel[wheelPos.hour].push_back(x);
					}
					else if (wheelPos.minute >= 0) {
						mtw->minuteWheel[wheelPos.minute].push_back(x);
					}
					else if (wheelPos.second >= 0) {
						mtw->secondWheel[wheelPos.second].push_back(x);
					}
				}
			}
			mtw->secondWheel[(int)mtw->secondCurPos].clear();
		}
		else if (ret == -1) {
			if (errno == EINTR || errno == EAGAIN) {
				continue;
			}
			perror("epoll error:");
			abort();
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值