当前位置:网站首页>Timer,时间堆
Timer,时间堆
2022-08-07 16:52:00 【programing菜鸟】
前言
博主这一段时间都没有更新博客,因为去抄 写了几个小的项目。接下来的几篇博客就当作是项目的总结吧。其中一个项目就是来自github的C++11版本WebServer。我实现的则放到了gitee上my_webserver 。
该项目应该也是吸取了最经典的TinyWebServer,主要由以下几个模块组成:
- 配置模块,一些基础的个性化配置。
- 日志模块,记录服务器信息,便于查看和修bug。
- 连接池,项目使用mysql数据库,使用连接池管理mysql连接。
- 线程池,执行任务。
- 时间堆,用于管理定时任务。
- epoller,linux下实现高并发的关键。
- 缓冲区,用于存储http请求和响应的数据。
- http模块,主要包括http请求,http响应,和管理http连接。
- webserver,服务器的主要逻辑。
接下来我会逐一分析markparticle的C++11版本的这些模块。在开始此系列博客之前,强烈推荐大家阅读游双大佬的《Linux高性能服务器编程》,关于这些模块的条条杠杠,不敢说100%,一大半的基础知识都来自这本书。
什么是定时器
服务器中的定时器和我们日常生活中的定时器概念一样,**即将一个事件与一个时间点绑定,时间点一到,就执行该事件。**比如,我想明天8点起床,就定了一个8点的定时器。当闹钟一响,我就会执行起床这个事件。那么,在编程中,时间点很容易表示,如何刻画一个事件呢?没错,就是函数。
void get_up(){
}

这样就能在特定的时间点,执行特定的事件。
当然了,基于不同的任务,我们的时间点设置也会不同,也可能会有半小时以后,2天以后这样的相对时间点,也可能会有绝对时间点。
定时器的实现
服务器往往需要很多个定时任务,这时候就需要一种数据结构管理它们,这就是服务器需要时间管理器的原因。
如果有很多个定时任务,应该怎样管理它们呢?
显然,我们需要基于它们触发的时间先后进行排序,排在前面的事件先触发。而触发的规则又有不同。比如,可以让时间管理器按照一定的周期进行触发,每隔5s触发一次之类的。但是,这样的坏处就是,可能每次触发不一定有事件就绪,白白触发。基于这样一种考虑,我们设法获得下一次触发任务到现在的时间t,然后让时间管理器经过t之后去触发任务,此时至少有一个任务会被触发。
时间堆
作者实现的时间管理器使用的是堆。利用堆的性质,每次可以选取出最值的特点,每次选择时间节点最小的定时器出来执行,以此往复。
而关于时间方面,使用的是C++11引入的chrono库。
typedef std::function<void()> TimeoutCallBack; //回调函数
typedef std::chrono::high_resolution_clock Clock; //时钟
typedef std::chrono::milliseconds MS; //毫秒
typedef Clock::time_point TimeStamp; //时间戳
struct TimerNode{
//时间节点
int id;
TimeStamp expires; //绝对时间
TimeoutCallBack cb;
bool operator<(const TimerNode& rhs){
//用于比较
return expires < rhs.expires;
}
};
这里,先typedef了一些类型用于后续方便使用。也定义了时间结点。你可能会注意到,回调函数的类型是function<void()>,一个没有参数且没有返回值的函数。那么,如果你的定时任务需要参数怎么办?
我们默认回调函数没有返回值。因为回调函数可能带有任意类型的参数,所以干脆将其变成没有参数的,如果你的回调函数带有参数,你需要自己使用bind或者lambda进行封装。这个手法经常使用。
- id,用于标识一个时间结点,用来调整或者删除。
- expires,就是定时器触发的时间,这里使用的是时间戳。
- cb,即时间到之后要执行的任务。
对于时间堆,我们采用数组形式。在插入,调整,删除时间堆时,往往需要获得它们的下标,所以使用哈希存储时间节点id到数组下标的映射关系。
class Timer{
public:
//...
private:
std::vector<TimerNode> heap_;
std::unordered_map<int, int> ref_; //id -> index
};
而对于堆,自然要提供向下调整和向上调整的函数。而为了更好的调整ref_,手写了一个swap函数。
//向下调整法,在[index, n)中调整。不包括n!!!
bool Timer::SiftDown_(int index, size_t n);
//向上调整,原作者的size_t类型的index,可能在这里会有一个bug。
void Timer::SiftUp_(int index);
//交换下标i,j处的值,并调整ref_的映射。
void Timer::swap(int i, int j);
- 时间管理器肯定要提供一个增加定时器的函数add。该函数会向堆中增加一个定时器,如果该定时器已经存在,那么就更新该定时器。
- 也提供了调整定时器时间的函数,用新的时间刷新指定定时器。
- 删除定时器,暴露给外部的接口只会删除堆顶节点。该函数回去调用del_。
//增加定时器,如果定时器已经存在,则用new_expires和cb去更新该定时器。
void add(int id, int new_expires, const TimeoutCallBack& cb);
//调整定时器的触发时间
void adjust(int id, int timeout);
//删除堆顶节点
void pop();
//删除位置为index的节点。私有函数,不会被外部调用。
void del_(int index);
- 时间堆当然需要工作函数,用来触发到点的时间节点。
- 需要一个tick心跳函数,去检查时间堆,清除所有过期节点。
- 需要一个GetNextTick()函数,去获得下一次触发的时间,确保每次触发都会有事件就绪。
void worker(int id);
void tick(); //心跳函数
int GetNextTick();
边栏推荐
猜你喜欢

笔记本接显卡怎么操作 笔记本外接显卡详细教程

怎样设置电脑无线网络 如何设置无线网络连接

Common data interview questions

Does the laptop have bluetooth? How to turn on bluetooth on the laptop

2.11-2.10 - storage management page type store 2.12 sections 2.13 - period of storage

Win7怎么看硬盘大小 如何看电脑硬盘大小

qq聊天截图怎么截长图 使用qq长截图的图文步骤

Analysis of various related architectures of MCU

win7查询物理地址方法 win7怎么查物理地址

怎么清理电脑C盘 win7清理电脑c盘垃圾文件方法介绍
随机推荐
压缩文件如何加密码_压缩文件加密码怎么设置
UICamrea和其他Camera的区别,Camera跟随人物移动的脚本,人物移动结合遥感
2022-08-06 第四小组 修身课 学习笔记(every day)
Win7怎么看硬盘大小 如何看电脑硬盘大小
win10检测不到U盘如何解决_win10系统识别不了U盘怎么解决
【论文理解】Batch Normalization论文中关于BN背景和减少内部协变量偏移的解读(论文第1、2节)
Unity Webgl发布的一些注意的点
项目管理仅关注交付结果不够,价值实现是未来趋势
R语言ggplot2可视化:使用ggpubr包的ggtexttable函数可视化表格数据(直接绘制表格图或者在图像中添加表格数据)、使用tab_add_title为表格数据添加副标题(subtitle
网线连不上网怎么回事 为什么网线插电脑上不了网
ELK日志平台搭建(一)
What to do if the computer can't delete the folder
怎样设置电脑无线网络 如何设置无线网络连接
minium小程序自动化测试
调整台式电脑屏幕亮度的步骤 台式机显示器亮度怎么调
函数节流与函数防抖
深度解析期权现货合约交易所系统开发说明分析
为什么qq在电脑上登录不了 电脑有网络登不上qq如何处理
R语言ggplot2可视化:使用ggpubr包的ggdotchart函数可视化分组克利夫兰点图(Cleveland dot)、自定义position参数设置不同分组的数据点分离(程度)
网上股票开户和线下开户一样安全吗