定时器介绍和实现

1.定时器模块构成

1.1组织方式:

添加任务、有效地组织方式、数据结构
可以根据过期时间/执行顺序组织定时任务

根据执行序组织任务

根据执行序组织定时任务
实现一个时间移动的机制

addTimer(1000,func)//将任务挂到对应节点上
addTimer(2000,func)//
addTimer(3000,func)

触发方式:
时间指针不断移动,当移动到挂有任务的节点时,就把该节点的所有任务都处理掉

1.2 根据过期时间组织任务

触发方式:
根据数据结构找到最小值(即最近要触发的定时任务)

struct TimerNode{
time_t expire;//过期时间:now()+time
func_t func;//处理定时任务
void* ctx;//作为函数参数,内容为func依赖的上下文
};
{ int i=10;
Role * role;
addTimer(1000,func);}

异步行为:不占用线程方式实现定时器
不会阻塞,在到达过期时间时调用func,所以要保存上下文

1.3检测机制:希望快速找到最小值

红黑树
最小堆:描述父子之间的关系(父节点小于子节点)
组织结构:不需要完全有序,只需要相对有序

2.定时器模块驱动方式

按照触发时间有序组织:
now() 与 min(TimerNode expire) 比较
按照执行序进行组织
实现时间指针移动的机制
但不管是上面哪种方式都不可能一直占用时间片进行检测
驱动方式:间隔时间后再来比较

2.1跟网络模块协同处理

A. epoll_wait

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
//1) int epfd: epoll_create()函数返回的epoll实例的句柄。  
//2) struct epoll_event * events: 接口的返回参数,epoll把发生的事件的集合从内核复制到 events数组中。events数组是一个用户分配好大小的数组,数组长度大于等于maxevents。
//3) int maxevents: 表示本次可以返回的最大事件数目,通常maxevents参数与预分配的events数组的大小是相等的。  
//4) int timeout: 表示在没有检测到事件发生时最多等待的时间,超时时间(>=0),单位是毫秒ms,-1表示阻塞,0表示不阻塞。

这里通过timeout将网络模型和定时事件联系起来
下图为redis处理定时任务方式

//如果在一定时间内没有收到网络时间,就可以处理定时任务了 
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);//tvp是查看定时任务过期时间,如果大于0,则将检测时间置为当前定时任务最小的过期时间

B.希望把定时任务当作IO一样,按照事件处理

timefd

C.usleep 占用一个线程,检测所有的定时器的任务

2.2定时器C++实现

#include <time.h>
#include<sys/epoll.h>
#include<iostream>
#include<functional>
#include <set>
#include<memory>
#include<unistd.h>
using namespace std;
struct TimerBaseNode  {
	time_t expire;
	int64_t id;
};
struct TimerNode : public   TimerBaseNode {
	using CallBack = std::function<void(const TimerNode & node)>;
	CallBack func;
	TimerNode(int64_t id, time_t expire, CallBack func) :func(std::move(func)) {
		this->expire = expire;
		this->id = id;
	}
};
bool operator<(const TimerBaseNode &ld, const TimerBaseNode &rd) {
	if (ld.expire < rd.expire) {//定时任务排序方式:如果过期时间相同,那么要按照插入id比较
		return true;
	}
	else if (ld.expire > rd.expire) return false;
	else return ld.id < rd.id;
}
class Timer {
public:
	static inline time_t GetTick() {
		uint64_t t;
		struct timespec ti;
		clock_gettime(CLOCK_MONOTONIC, &ti);
		t = (uint64_t)ti.tv_sec * 1000;
		t += ti.tv_nsec / 1000000;
		return t;
	}
	TimerBaseNode AddTimer(time_t msec, TimerNode::CallBack func) {
		time_t expire = GetTick() + msec;
		auto pairs=timeouts.emplace(GetID(), expire, std::move(func));
		return static_cast<TimerBaseNode>(*pairs.first);
	}
	bool DelTimer(TimerBaseNode & node) {
		auto iter = timeouts.find(node);
		if (iter != timeouts.end()) {
			timeouts.erase(iter);
			return true;
		}
		return false;
	}
	void HandleTimer(time_t now) {
		auto iter = timeouts.begin();
		while (iter != timeouts.end() && iter->expire <= now) {
			iter->func(*iter);
			iter = timeouts.erase(iter);
			continue;
		}
	}
	int TimeToSleep() {//计算定时事件最小过期时间和当前时间的差值
		//如果在该时间段内没有网络事件到达就去处理定时任务
		auto iter = timeouts.begin();
		if (iter == timeouts.end()) {
			return -1;
		}
		int diss = iter->expire - GetTick();
		return diss > 0 ? diss : 0;
	}
private:
	static inline int64_t GetID() {
		return gid++;
	}
	static int64_t gid;
	set <TimerNode,std::less<>> timeouts;//set底层为红黑树
};
int64_t Timer::gid = 0;
int main() {
	int epfd = epoll_create(1);
	unique_ptr<Timer> timer = make_unique<Timer>();
	epoll_event ev[256] = {};
	int i = 0;
	timer->AddTimer(1000, [&](const TimerNode & node) {
		i++;
		cout << "node id: " << node.id <<"hello yajie i is" << i<< endl;
	});
	while (true) {
		int n = epoll_wait(epfd, ev, 256, timer->TimeToSleep());
		time_t now = Timer::GetTick();
		for (int i = 0; i < n; i++) {
			//处理网络事件
		}
		//处理定时事件
	}
	close(epfd);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值