Linux网络设计之手写定时器timer

定时器应用场景

(1)心跳检测。
(2)游戏中的技能冷却。
(3)倒计时。
(4)其他需要延迟处理的功能。

红黑树

红黑树是绝对有序的数据结构。

1
2
3
4
5
6
7
8
9
header

利用红黑树实现定时器

在c++中,set、map、multiset、multimap使用的是红黑树管理数据。可以利用这几个类实现定时器方案,以set为例,使用C++ 14特性。

实现接口

«interface»
Timer
time_t GetTick()
TimerNodeBase AddTimer(time_t msec,CallBack func)
bool DelTimer(TimerNodeBase &node)
bool CheckTimer()
time_t TimeToSleep()

获取当前时间的接口GetTick()

C++ 11时间库chrono
(1)steady_clock:系统启动到当前的时间,可以用来计算程序运行时间。
(2)system_clock:时间戳,可以修改。
(3)high_resolution_clock:高精度版本的steady_lock。

int64_t GetTick()
{
	auto sc=chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
	auto temp=chrono::duration_cast<milliseconds>(sc.time_since_epoch());
	return temp.count();
}

相同触发时间的定时任务处理方案

(1)越后面插入的节点,插入位置放在红黑树越右侧。
(2)使用C++的set容器;内部是红黑树管理数据。
(3)定时器节点设计,使用触发时间和ID唯一标识定时节点。

struct TimeNodeBase{
int64_t id;		// 描述插入的先后顺序
time_t  expire;	// 触发时间
}

(4)比较仿函数,确定数据插入红黑树的位置。利用C++14的特性,find时只需要等价key比较,无需构建key对象比较。利用基类的多态特性,只需要一个比较仿函数即可。

bool operator < (const TimeNodeBase *lhd,const TimeNodeBase *rhd)
{
	if(lhd->expire < rhd->expire)
		return true;
	else if(lhd->expire > rhd->expire)
		return false;
	return lhd->id < rhd->id;
}

(5)尽量减少函数对象赋值、移动等操作,提高性能。

// TimerNode 继承 TimerNodeBase
struct TimerNode:public TimerNodeBase
{
	// C++ 11特性,使用函数对象。降低拷贝消耗,提高效率
	using Callback = std::function<void(const TimerNode &node)>;
	Callback func;

	// 构造函数,只构造一次
	TimerNode(int64_t id,time_t expire,Callback func):func(func){
		this->id = id;
		this->expire = expire;

	}
};

驱动定时器方式

可以采用IO时间和定时器事件在同一个线程执行的方案,利用epoll的epoll_wait()第四个参数做定时延时。
示例:

int main()
{
	int epfd=epoll_wait(1);
	epoll_event evs[64]={0};
	while(1)
	{
		int n=epoll_wait(epfd,evs,64,delaytime);
		int i=0;
		for(i=0;i<n;i++)
		{
			/*处理IO事件*/
		}
		// 处理定时任务事件
	}
	return 0;
}

示例代码

(1)创建定时器驱动,epoll_create、epoll_wait。
(2)创建timer类,实现AddTimer()、CheckTimer()、DelTimer()等接口。
(3)选择数据结构,set容器,本质使用红黑树数据结构。
(4)定义节点的结构。
demo代码:

#include <sys/epoll.h>
#include <functional>
#include <chrono>
#include <set>
#include <memory>
#include <iostream>

using namespace std;

struct TimerNodeBase
{
	time_t expire;
	int64_t id;
};

// TimerNode 继承 TimerNodeBase
struct TimerNode:public TimerNodeBase
{
	// C++ 11特性,使用函数对象。降低拷贝消耗,提高效率
	using Callback = std::function<void(const TimerNode &node)>;
	Callback func;

	// 构造函数,只构造一次
	TimerNode(int64_t id,time_t expire,Callback func):func(func){
		this->id = id;
		this->expire = expire;

	}
};

// 基类引用,多态特性
bool operator<(const TimerNodeBase &lhd, const TimerNodeBase &rhd)
{
	if (lhd.expire < rhd.expire)
		return true;
	else if (lhd.expire > rhd.expire)
		return false;
	return lhd.id < rhd.id;
}

class Timer
{
public:
	static time_t GetTick()
	{
		/* C++ 11时间库chrono */
		//表示一个具体时间
		auto sc = chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
		auto tmp = chrono::duration_cast<chrono::milliseconds>(sc.time_since_epoch());
		return tmp.count();
	}

	TimerNodeBase AddTimer(time_t msec, TimerNode::Callback func)
	{
		time_t expire = GetTick() + msec;
		//避免拷贝、移动构造
		auto ele = timermap.emplace(GenID(), expire, func);

		return static_cast<TimerNodeBase>(*ele.first);
	}

	bool DelTimer(TimerNodeBase &node)
	{
		// C++ 14新特性,不在需要传一个key对象,传递一个key的等价值
		auto iter = timermap.find(node);
		if (iter != timermap.end())
		{
			timermap.erase(iter);
			return true;
		}
		return false;
	}

	bool CheckTimer()
	{
		auto iter = timermap.begin();

		if (iter != timermap.end() && iter->expire <= GetTick())
		{
			iter->func(*iter);
			timermap.erase(iter);
			return true;
		}
		return false;
	}

	time_t TimeToSleep()
	{
		auto iter = timermap.begin();
		if (iter == timermap.end())
			return -1;//没有定时任务,设置epoll一直阻塞。
		time_t diss = iter->expire - GetTick();

		return diss > 0 ? diss : 0;
	}
private:
	static int64_t GenID()
	{
		return gid++;
	}

	static int64_t gid;
	set<TimerNode, std::less<>> timermap;
};

int64_t Timer::gid = 0;

#define EPOLL_EV_LENFTH	1024


int main()
{
	int epfd = epoll_create(1);

	unique_ptr<Timer> timer = make_unique<Timer>();

	int i = 0;
	timer->AddTimer(1000, [&](const TimerNode &node) {
		cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
	});

	timer->AddTimer(1000, [&](const TimerNode &node) {
		cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
	});

	timer->AddTimer(3000, [&](const TimerNode &node) {
		cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
	});

	auto node = timer->AddTimer(2100, [&](const TimerNode &node) {
		cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
	});

	timer->DelTimer(node);

	cout << "now time:" << Timer::GetTick() << endl;

	epoll_event evs[EPOLL_EV_LENFTH] = { 0 };

	while (1)
	{
		int nready = epoll_wait(epfd, evs, EPOLL_EV_LENFTH, timer->TimeToSleep());
		for (int i = 0; i < nready; i++)
		{
			/*处理IO事件*/
		}

		// timer检测和处理
		while (timer->CheckTimer());
	}

	return 0;
}

总结

设计一个定时器时,先确定时间精度;选择驱动定时器的方式;选择合适的数据接口(不要选择优先队列);设计定时器基本接口 { AddTimer()、DelTimer()、CheckTimer() } 和扩展接口 { Tick()等 } ;考虑相同触发时间的定时任务处理。

确定
选择
定时器设计
驱动方式
数据结构
设计接口
相同触发事件的定时任务处理
是否支持lambda表达式
AddTimer
DelTimer
CheckTimer
UpdateTimer
......

后言

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接,详细查看详细的服务:C/C++服务器

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux 定时器 timer_list 是一个内核数据结构,用于管理内核中的定时器。它是一个双向链表,每个节点表示一个定时器timer_list 的定义位于 `<linux/timer.h>` 头文件中。 每个 timer_list 节点的定义如下: ```c struct timer_list { struct list_head entry; // 定时器节点的链表指针 unsigned long expires; // 定时器的到期时间 void (*function)(unsigned long); // 定时器回调函数 unsigned long data; // 传递给回调函数的参数 struct tvec_base *base; // 定时器所属的时间轮 int slack; // 定时器的松弛时间 }; ``` 其中,`entry` 是一个 `list_head` 结构,用于将节点连接到定时器链表中。`expires` 表示定时器的到期时间,以 jiffies 单位表示。`function` 是定时器的回调函数,在定时器到期时被调用。`data` 是传递给回调函数的参数。`base` 表示定时器所属的时间轮,`slack` 是定时器的松弛时间,用于处理定时器的精度。 在使用 timer_list 时,可以使用以下函数进行初始化和操作: - `timer_setup(struct timer_list *timer, void (*function)(unsigned long), unsigned int flags)`:初始化一个定时器,并指定回调函数和标志。 - `init_timer(struct timer_list *timer)`:初始化一个定时器。 - `add_timer(struct timer_list *timer)`:将定时器添加到定时器链表中。 - `del_timer(struct timer_list *timer)`:从定时器链表中删除定时器。 - `mod_timer(struct timer_list *timer, unsigned long expires)`:修改定时器的到期时间。 这些函数可以通过 `<linux/timer.h>` 头文件中的宏来调用。通过操作 timer_list,可以实现在 Linux 内核中的定时器功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值