【服务器学习】timer定时器模块

timer定时器模块

以下是从sylar服务器中学的,对其的复习;
参考资料

定时器概述

通过定时器可以实现给服务器注册定时事件,这是服务器上经常要处理的一类事件,比如3秒后关闭一个连接,或是定期检测一个客户端的连接状态。
定时事件依赖于Linux提供的定时机制,它是驱动定时事件的原动力,目前Linux提供了以下几种可供程序利用的定时机制:

  1. alarm()或setitimer(),这俩的本质都是先设置一个超时时间,然后等SIGALARM信号触发,通过捕获信号来判断超时
  2. 套接字超时选项,对应SO_RECVTIMEO和SO_SNDTIMEO,通过errno来判断超时
  3. 多路复用超时参数,select/poll/epoll都支持设置超时参数,通过判断返回值为0来判断超时
  4. timer_create系统接口,实质也是借助信号,参考man 2 timer_create
  5. timerfd_create系列接口,通过判断文件描述符可读来判断超时,可配合IO多路复用
  • sylar使用时间堆的方式管理定时器

每次都取出所有定时器中超时时间最小的超时值作为一个tick,这样,一旦tick触发,超时时间最小的定时器必然到期。处理完已超时的定时器后,再从剩余的定时器中找出超时时间最小的一个,并将这个最小时间作为下一个tick,如此反复,就可以实现较为精确的定时。
最小堆很适合处理这种定时方案,将所有定时器按最小堆来组织,可以很方便地获取到当前的最小超时时间,sylar采取的即是这种方案。

sylar定时器设计

sylar的定时器采用最小堆设计,所有定时器根据绝对的超时时间点进行排序,每次取出离当前时间最近的一个超时时间点,计算出超时需要等待的时间,然后等待超时。超时时间到后,获取当前的绝对时间点,然后把最小堆里超时时间点小于这个时间点的定时器都收集起来,执行它们的回调函数。

关于定时器和IO协程调度器的整合。IO协程调度器的idle协程会在调度器空闲时阻塞在epoll_wait上,等待IO事件发生。在之前的代码里,epoll_wait具有固定的超时时间,这个值是5秒钟。加入定时器功能后,epoll_wait的超时时间改用当前定时器的最小超时时间来代替。epoll_wait返回后,根据当前的绝对时间把已超时的所有定时器收集起来,执行它们的回调函数。

由于epoll_wait的返回并不一定是超时引起的,也有可能是IO事件唤醒的,所以在epoll_wait返回后不能想当然地假设定时器已经超时了,而是要再判断一下定时器有没有超时,这时绝对时间的好处就体现出来了,通过比较当前的绝对时间和定时器的绝对超时时间,就可以确定一个定时器到底有没有超时。

sylar定时器实现

sylar的定时器对应Timer类,这个类的成员变量包括定时器的绝对超时时间点,是否重复执行,回调函数,以及一个指向TimerManager的指针,提供cancel/reset/refresh方法用于操作定时器。构造Timer时可以传入超时时间,也可以直接传入一个绝对时间。Timer的构造函数被定义成私有方式,只能通过TimerManager类来创建Timer对象。除此外,Timer类还提供了一个仿函数Comparator,用于比较两个Timer对象,比较的依据是绝对超时时间。

class TimerManager;
/*** @brief 定时器*/
class Timer : public std::enable_shared_from_this<Timer> {
friend class TimerManager;
public:/// 定时器的智能指针类型typedef std::shared_ptr<Timer> ptr;/*** @brief 取消定时器*/bool cancel();/*** @brief 刷新设置定时器的执行时间*/bool refresh();/*** @brief 重置定时器时间* @param[in] ms 定时器执行间隔时间(毫秒)* @param[in] from_now 是否从当前时间开始计算*/bool reset(uint64_t ms, bool from_now);
private:/*** @brief 构造函数* @param[in] ms 定时器执行间隔时间* @param[in] cb 回调函数* @param[in] recurring 是否循环* @param[in] manager 定时器管理器*/Timer(uint64_t ms, std::function<void()> cb,bool recurring, TimerManager* manager);/*** @brief 构造函数* @param[in] next 执行的时间戳(毫秒)*/Timer(uint64_t next);
private:/// 是否循环定时器bool m_recurring = false;/// 执行周期uint64_t m_ms = 0;/// 精确的执行时间uint64_t m_next = 0;/// 回调函数std::function<void()> m_cb;/// 定时器管理器TimerManager* m_manager = nullptr;
private:/*** @brief 定时器比较仿函数*/struct Comparator {/*** @brief 比较定时器的智能指针的大小(按执行时间排序)* @param[in] lhs 定时器智能指针* @param[in] rhs 定时器智能指针*/bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const;};
};

所有的Timer对象都由TimerManager类进行管理,TimerManager包含一个std::set类型的Timer集合,这个集合就是定时器的最小堆结构,因为set里的元素总是排序过的,所以总是可以很方便地获取到当前的最小定时器。TimerManager提供创建定时器,获取最近一个定时器的超时时间,以及获取全部已经超时的定时器回调函数的方法,并且提供了一个onTimerInsertedAtFront()方法,这是一个虚函数,由IOManager继承时实现,当新的定时器插入到Timer集合的首部时,TimerManager通过该方法来通知IOManager立刻更新当前的epoll_wait超时。TimerManager还负责检测是否发生了校时,由detectClockRollover方法实现。

/*** @brief 定时器管理器*/
class TimerManager {
friend class Timer;
public:/// 读写锁类型typedef RWMutex RWMutexType;/*** @brief 构造函数*/TimerManager();/*** @brief 析构函数*/virtual ~TimerManager();/*** @brief 添加定时器* @param[in] ms 定时器执行间隔时间* @param[in] cb 定时器回调函数* @param[in] recurring 是否循环定时器*/Timer::ptr addTimer(uint64_t ms, std::function<void()> cb,bool recurring = false);/*** @brief 添加条件定时器* @param[in] ms 定时器执行间隔时间* @param[in] cb 定时器回调函数* @param[in] weak_cond 条件* @param[in] recurring 是否循环*/Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb,std::weak_ptr<void> weak_cond,bool recurring = false);/*** @brief 到最近一个定时器执行的时间间隔(毫秒)*/uint64_t getNextTimer();/*** @brief 获取需要执行的定时器的回调函数列表* @param[out] cbs 回调函数数组*/void listExpiredCb(std::vector<std::function<void()> >& cbs);/*** @brief 是否有定时器*/bool hasTimer();
protected:/*** @brief 当有新的定时器插入到定时器的首部,执行该函数*/virtual void onTimerInsertedAtFront() = 0;/*** @brief 将定时器添加到管理器中*/void addTimer(Timer::ptr val, RWMutexType::WriteLock& lock);
private:/*** @brief 检测服务器时间是否被调后了*/bool detectClockRollover(uint64_t now_ms);
private:/// MutexRWMutexType m_mutex;/// 定时器集合std::set<Timer::ptr, Timer::Comparator> m_timers;/// 是否触发onTimerInsertedAtFrontbool m_tickled = false;/// 上次执行时间uint64_t m_previouseTime = 0;
};

IOManager通过继承的方式获得TimerManager类的所有方法,这种方式相当于给IOManager外挂了一个定时器管理模块。为支持定时器功能,需要重新改造idle协程的实现,epoll_wait应该根据下一个定时器的超时时间来设置超时参数。

class IOManager : public Scheduler, public TimerManager {
...
}void IOManager::idle() {SYLAR_LOG_DEBUG(g_logger) << "idle";// 一次epoll_wait最多检测256个就绪事件,如果就绪事件超过了这个数,那么会在下轮epoll_wati继续处理const uint64_t MAX_EVNETS = 256;epoll_event *events       = new epoll_event[MAX_EVNETS]();std::shared_ptr<epoll_event> shared_events(events, [](epoll_event *ptr) {delete[] ptr;});while (true) {// 获取下一个定时器的超时时间,顺便判断调度器是否停止uint64_t next_timeout = 0;if( SYLAR_UNLIKELY(stopping(next_timeout))) {SYLAR_LOG_DEBUG(g_logger) << "name=" << getName() << "idle stopping exit";break;}// 阻塞在epoll_wait上,等待事件发生或定时器超时int rt = 0;do{// 默认超时时间5秒,如果下一个定时器的超时时间大于5秒,仍以5秒来计算超时,避免定时器超时时间太大时,epoll_wait一直阻塞static const int MAX_TIMEOUT = 5000;if(next_timeout != ~0ull) {next_timeout = std::min((int)next_timeout, MAX_TIMEOUT);} else {next_timeout = MAX_TIMEOUT;}rt = epoll_wait(m_epfd, events, MAX_EVNETS, (int)next_timeout);if(rt < 0 && errno == EINTR) {continue;} else {break;}} while(true);// 收集所有已超时的定时器,执行回调函数std::vector<std::function<void()>> cbs;listExpiredCb(cbs);if(!cbs.empty()) {for(const auto &cb : cbs) {schedule(cb);}cbs.clear();}...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/148628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP学习:深入NLP

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 之前学过一段时间NLP,因为其中涉及到一些深度学习常用的知识或者框架,但苦于不系统以及没有任务focus不能长久.这里借助微软的教程写点东西. tokenization&&representation 将一句话中的单词分割就是分词(…

使用cgroup控制CPU使用率

关键文件 cpu子系统中的关键文件。 cpu.cfs_period_uscpu.cfs_quota_ustaskscgroup.procs 常用命令 查看当前系统内的CPU。 lscpu 查看当前系统内的CPU。 cat /proc/cpuinfo 查看当前的子系统。 lssubsys -a 将进程加入到控制组内。 echo PID > tasks或者 echo PID &…

Python之冒泡排序(AI自动写文章项目测试)

全自动AI生成文章测试&#xff0c;如有不合理地方&#xff0c;请见谅。 一、冒泡排序简介 1.1 冒泡排序概述 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;通过不断交换相邻元素的位置&#xff0c;将最大&#xff08;或最小&#xff09;的元…

Rust开发——使用rust实现Redis中hset

一、Redis中hset HSET 是 Redis 中用于在哈希数据结构中设置指定字段的值的命令。哈希是一种类似于字典或映射的数据结构&#xff0c;它存储了键值对的集合&#xff0c;其中每个键都包含多个字段和与这些字段相关联的值。 哈希表在 Redis 中以键值对形式存储&#xff0c;并通…

【产品应用】一体化伺服电机在系留无人机中的应用

一体化伺服电机是一种将电机、驱动器、编码器结合在一起的伺服系统&#xff0c;具有高精度控制、快速响应和高效运行等优点。系留无人机则是一种通过绳索或链条与地面设施连接的无人机&#xff0c;能够实现长时间的稳定悬停和空中作业。 01.设备简介 电源线牵引装置&#xff1…

uniapp如何使用api相关提示框

uni.showToast&#xff1a;用于显示一条带有图标的提示框。title&#xff1a;提示的内容。icon&#xff1a;图标&#xff0c;可选值包括 success、loading、none。duration&#xff1a;提示框持续时间&#xff08;单位&#xff1a;毫秒&#xff09;&#xff0c;默认为1500。 un…

leetcode面试经典150题——29 三数之和

题目&#xff1a;盛最多水的容器 描述&#xff1a; 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意…

TG Pro v2.87(mac温度风扇速度控制工具)

TG Pro 是适用于 macOS 的温度和风扇速度控制工具&#xff0c;可让您监控 Mac 组件&#xff08;例如 CPU 和 GPU&#xff09;的温度和风扇速度。如果您担心 Mac 过热或想要手动调整风扇速度以降低噪音水平&#xff0c;这将特别有用。 除了温度和风扇监控&#xff0c;TG Pro 还…

C练习题_13

一、单项选择题(本大题共20小题&#xff0c;每小题2分&#xff0c;共40分。在每小题给出的四个备选项中&#xff0c;选出一个正确的答案&#xff0c;并将所选项前的字母填写在答题纸的相应位置上 以下叙述不正确的是&#xff08;)。 A.C程序中&#xff0c;语句之间必须用分号分…

【算法】二分查找-20231120

这里写目录标题 一、75. 颜色分类二、80. 删除有序数组中的重复项 II三、125. 验证回文串四、189. 轮转数组 一、75. 颜色分类 提示 中等 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff…

Asp.net MVC Api项目搭建

整个解决方案按照分层思想来划分不同功能模块&#xff0c;以提供User服务的Api为需求&#xff0c;各个层次的具体实现如下所示&#xff1a; 1、新建数据库User表 数据库使用SQLExpress版本&#xff0c;表的定义如下所示&#xff1a; CREATE TABLE [dbo].[User] ([Id] …

AI机器学习 | 基于librosa库和使用scikit-learn库中的分类器进行语音识别

专栏集锦&#xff0c;大佬们可以收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https:/…

​软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第17章 通信系统架构设计理论与实践&#xff08;P614~646&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

文旅媒体有哪些?如何邀请到现场报道?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 中国文旅产业在近年来得到了持续而快速的发展。从产业端看&#xff0c;中国文旅产业呈现出新的发展趋势&#xff0c;其中“文旅”向“文旅”转变成为显著特点。通过产业升级和空间构建&a…

【项目管理】中途接手的项目应对实用指南

导读&#xff1a;作为项目经理中途接手项目往往不可避免&#xff0c;为了保证项目成功需要项目经理额外考虑更多的因素和处理相关问题&#xff0c;也往往带来很大的挑战性。本文提供可应对借鉴的思路&#xff0c;在一定程度上可以作为最佳实践。 目录 1、首先、了解项目项目背…

2023.11.17 关于 Spring Boot 日志文件

目录 日志文件作用 常见的日志框架说明 门面模式 日志的使用 日志的级别 六种级别 日志级别的设置 日志的持久化 使用 Lombok 输出日志 实现原理 普通打印和日志的区别 日志文件作用 记录 错误日志 和 警告日志&#xff08;发现和定位问题&#xff09;记录 用户登录…

全新云开发工具箱:融合多项功能的微信小程序源码解决方案

全新云开发工具箱&#xff1a;融合多项功能的微信小程序源码解决方案 这款微信小程序源码提供了超过40个功能&#xff0c;集合了各种实用工具&#xff0c;成为一款全能工具箱。这些功能包括证件照制作、垃圾分类查询、个性签名制作、二维码生成、文字九宫格、手持弹幕、照片压…

飞鼠异地组网工具实战之访问k8s集群内部服务

飞鼠异地组网工具实战之访问k8s集群内部服务 一、飞鼠异地组网工具介绍1.1 飞鼠工具简介1.2 飞鼠工具官网 二、本次实践介绍2.1 本次实践场景描述2.2 本次实践前提2.3 本次实践环境规划 三、检查本地k8s集群环境3.1 检查k8s各节点状态3.2 检查k8s版本3.3 检查k8s系统pod状态 四…

python趣味编程-5分钟实现一个打字速度测试(含源码、步骤讲解)

Python速度打字测试是用 Python 编程语言编写的,速度打字测试 Python项目理念,我们将构建一个令人兴奋的项目,通过它您可以 检查 甚至 提高 您的打字速度。 为了创建图形用户界面(GUI),我们将使用 用于处理图形的pygame库。 Python 打字速度测试有利于学生或初学者提高…

Python大数据之linux学习总结——day10_hive调优

hive调优 hive调优hive命令和参数配置1.hive数据压缩压缩对比开启压缩 2.hive数据存储[练习]行列存储原理存储压缩比拓展dfs -du -h 3. fetch抓取4. 本地模式5. join的优化操作6. 列裁剪7. 分区裁剪8. group by 操作9. count(distinct)10. 笛卡尔积11. 动态分区[练习]12. 如何调…