一、定时器

1、定时器是什么?

定时器不仅存在于硬件领域,在软件层面(客户端、网页和服务端)也普遍应用,核心功能都是高效管理大量延时任务。不同应用场景下,其实现方式和使用方法有所差异。

2、定时器解决了什么问题?

可以定期清理缓存,定时备份数据,在服务器负载较大时,自动执行一些重要的功能,提高服务器的效率和稳定性。

3、是怎么解决的?

  • 组织大量延时任务的数据结构(容器)
  • 触发最近将超时的任务的机制

4、实现方式:

  • 对任务按触发时间进行排序:红黑树(map,set,multimap,multiset)–nginx,最小堆–libevent,libev,go,应用在单线程场景下
    • 1、触发时刻作为key,任务作为val
    • 2、快速找到最近要超时的任务
    • 3、触发后要删除该任务且支持随时删除任务
    • 4、允许相同时刻触发任务
  • 对执行顺序进行组织:时间轮,针对当前时间指针做偏移。–netty,skynet,kafka,应用在多线程场景下

5、有哪些常用触发机制?

  • I/O多路复用的最后一个超时参数
  • 将定时器转化为io处理,timerfd

6、使用场景

  • 与网络模块协同处理
  • 基于事件驱动业务开展
  • 除了协同网络处理,复用系统调用

二、具体实现

1、采用红黑树,对任务按触发时间进行排序

  • map<key, value>: 以key存触发时间,value存任务,那么可能存在多个同一时刻的任务,不选
  • multimap<key, value>:可能存储重复的值,然后操作起来比较麻烦,不选
  • set: 不可能出现重复的,可以
    那么以一个自定义结构作为key值进行存储,并且按触发时间进行排序
typedef struct TimerNode_S{time_t expire;//过期时间uint64_t id;    //由于可能存在多个定时器在同一时间过期,所以需要一个唯一标识}TimerNode_S;bool operator < (const TimerNode_S& lhd, const TimerNode_S& rhd)
{if(lhd.expire < rhd.expire){return true;}else if(lhd.expire > rhd.expire){return false;}else{      //如果相等,谁先插入,谁就先执行return lhd.id < rhd.id;}
}set<TimerNode, less<>> timeouts;

2、计算最近触发的定时任务离当前还有多久?

time_t TimeOut() 
{auto iter = timeouts.begin();if (iter == timeouts.end()) {return -1;}time_t t = iter->expire - GetTick();return t > 0 ? t : 0;
}

3、获取当前时间

/**
* @brief 获取当前时间的时间戳(以毫秒为单位)
*
* 使用 std::chrono::steady_clock 获取从系统启动到当前的时间
* std::chrono::system_clock,受系统时间影响,可能会被修改
*
* @return 返回当前时间的时间戳(以毫秒为单位)
*/
static inline time_t GetTick()                  
{return chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count();   
}

4、添加定时器

/**
* @brief 添加定时器
*
* 将一个定时器节点添加到定时器列表中,并返回该节点的标识。
*
* @param msec 定时器超时时间,单位为毫秒
* @param cb 定时器超时时执行的回调函数
*
* @return 返回定时器的标识,类型为 TimerNode_S
*/
TimerNode_S AddTimer(int msec, TimerNode::Callback cb)
{time_t expire = GetTick() + msec;       //过期时间if(timeouts.empty() || expire <= timeouts.crbegin()->expire){auto pairs = timeouts.emplace(GetID(), expire, move(cb));return static_cast<TimerNode_S>(*pairs.first);}auto ele = timeouts.emplace_hint(timeouts.crbegin().base(), GetID(), expire, move(cb));return static_cast<TimerNode_S>(*ele);
}

5、删除定时器

/**
* @brief 删除定时器节点
*
* 从定时器集合中删除指定的定时器节点。
*
* @param node 需要删除的定时器节点
*/
void DelTimer(TimerNode_S& node)
{auto iter = timeouts.find(node);if(iter != timeouts.end()){timeouts.erase(iter);}
}

6、处理定时器任务

/**
* @brief 处理超时事件
*
* 该函数遍历超时事件列表,对于已超时的每个事件,调用其回调函数进行处理,并从列表中移除该事件。
*
* @param now 当前时间戳
*/
void HandleTimeout(time_t now)
{auto iter = timeouts.begin();while(iter != timeouts.end() && iter->expire <= now){iter->cb(*iter);iter = timeouts.erase(iter);}
}struct epoll_event evs[64] = {0};
while(true){int n = epoll_wait(epfd, evs, 64, timer->TimeOut());time_t now = CTimer::GetTick();for(int j = 0; j < n; ++j){cout<<"epoll_wait:"<<endl;}timer->HandleTimeout(now);
}

在这里插入图片描述

以上是采用I/O多路复用的最后一个超时参数,接下来更换成timerfd

7、主要调用的函数

/*
*功能:创建定时器
*clockfd: CLOCK_REALTIME-系统实时时钟,与系统时间同步,受用户手动修改时间影响。CLOCK_MONOTONIC-单调递增时钟,自系统启动以来的时间,不受系统时间调整的影响
*flags:TFD_NONBLOCK(非阻塞模式)和 TFD_CLOEXEC(在 exec 调用时自动关闭文件描述符)          
*
*/
timerfd_create(int clockfd, int flags);/*
* 功能:用于设置定时器的初始超时时间和后续周期时间
* fd:timerfd
* flags: 控制定时器行为的标志:
*       0-------绝对时间
*       TFD_TIMER_ABSTIME------表示相对时间
* new_value: 指定了定时器的初始超时时间和(可选的)后续周期时间
* old_value: 用于恢复定时器到之前的状态,常设为nullptr
*/
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

8、将之前的计算触发时间的方式改成timerfd

void UpdateTimerfd(const int fd) {struct timespec abstime;auto iter = timeouts.begin();if (iter != timeouts.end()) {abstime.tv_sec = iter->expire / 1000;abstime.tv_nsec = (iter->expire % 1000) * 1000000;} else {abstime.tv_sec = 0;abstime.tv_nsec = 0;}struct itimerspec its = {.it_interval = {},.it_value = abstime};timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, nullptr);
}int tfd = timerfd_create(CLOCK_MONOTONIC, 0);struct epoll_event evs[64] = {0};
while(true){timer->UpdateTimerfd(tfd);int n = epoll_wait(epfd, evs, 64, -1);time_t now = CTimer::GetTick();for(int j = 0; j < n; ++j){cout<<"epoll_wait:"<<endl;}timer->HandleTimeout(now);
}

在这里插入图片描述

三、总结

  • 定时器在程序中无处不在,无论是硬件,还是网页,客户端,服务端等。
  • 在服务端上合理地使用定时器,能提高服务器的效率和稳定性,如定时清理缓存,在服务器高负载情况下,自动执行一些重要的任务等。
  • 定时器的数据结构多种多样,有根据触发时间排序的红黑树,最小堆,也有根据执行顺序的时间轮。
  • 服务端常与网络模块协同处理
  • 服务端常基于事件驱动业务开展
  • 服务端除了协同网络处理,复用系统调用

代码:
Code
0voice·Github

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

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

相关文章

Mac版本Android Studio配置LeetCode插件

第一步&#xff1a;Android Studio里面找到Settings&#xff0c;找到Plugins&#xff0c;在Marketplace里面搜索LeetCode Editor。 第二步&#xff1a;安装对应插件&#xff0c;并在Tools->LeetCode Plugin页面输入帐号和密码。 理论上&#xff0c;应该就可以使用了。但是&a…

【ISP算法精粹】动手实战:用 Python 实现 Bayer 图像的黑电平校正

在数字成像领域&#xff0c;图像信号处理器&#xff08;ISP&#xff09;如同幕后英雄&#xff0c;默默将传感器捕获的原始数据转化为精美的图像。而黑电平校正&#xff0c;作为ISP预处理流程中的关键一环&#xff0c;直接影响着最终图像的质量。今天&#xff0c;我们就通过Pyth…

Oracle OCP与MySQL OCP认证如何选?

认证本质与定位差异 Oracle OCP Oracle OCP是Oracle公司推出的旗舰级数据库专家认证&#xff0c;专注于其核心的闭源商业数据库技术体系。核心领域包括RAC&#xff08;Real Application Clusters&#xff09;高可用集群、Data Guard容灾解决方案、Exadata数据库一体机集成以及…

MVVM、MVC的区别、什么是MVVM

一、什么是MVVM &#xff08;一&#xff09;定义 MVVM是Model - View - ViewModel的缩写&#xff0c;它是一种软件架构设计模式&#xff0c;主要用于构建用户界面。这种模式将应用程序分为三个主要部分&#xff1a; Model&#xff08;模型层&#xff09; 它是应用程序中负责…

【SpringCache 提供的一套基于注解的缓存抽象机制】

Spring 缓存&#xff08;Spring Cache&#xff09;是 Spring 提供的一套基于注解的缓存抽象机制&#xff0c;常用于提升系统性能、减少重复查询数据库或接口调用。 ✅ 一、基本原理 Spring Cache 通过对方法的返回结果进行缓存&#xff0c;后续相同参数的调用将直接从缓存中读…

HRI-2025 | 大模型驱动的个性化可解释机器人人机交互研究

作者&#xff1a;Ferran Gebelli 1 ^{1} 1, Lavinia Hriscu 2 ^{2} 2, Raquel Ros 1 ^{1} 1, Sverin Lemaignan 1 ^{1} 1, Alberto Sanfeliu 2 ^{2} 2, Anais Garrell 2 ^{2} 2单位&#xff1a; 1 ^{1} 1PAL Robotics&#xff0c; 2 ^{2} 2IRI (UPC-CSIC)论文标题&#xff1a;P…

Gitee Wiki:重塑关键领域软件研发的知识管理范式

在数字化转型浪潮席卷全球的当下&#xff0c;关键领域软件研发正面临前所未有的知识管理挑战。传统文档管理模式的局限性日益凸显&#xff0c;知识传承的断层问题愈发严重&#xff0c;团队协作效率的瓶颈亟待突破。Gitee Wiki作为新一代知识管理平台&#xff0c;正在通过技术创…

JVM 内存溢出 详解

内存溢出 内存溢出指的是内存中某一块区域的使用量超过了允许使用的最大值&#xff0c;从而使用内存时因空间不足而失败&#xff0c;虚拟机一般会抛出指定的错误。 在Java虚拟机中&#xff0c;只有程序计数器不会出现内存溢出的情况&#xff0c;因为每个线程的程序计数器只保…

dvwa8——SQL Injection(Blind)

由题目得这一关用盲注写 LOW: 先用bp抓包一下 , 看到这low是get提交 , f12打开hackbar 输入?id1时报错 尝试闭合 , 回显正常 开始注入 1.order by 判断列数,3的时候开始回显报错,所以有两列 ?id1 order by 2--&SubmitSubmit# 2.无回显位置可以爆出,我们通过盲注来继…

探索分布式存储与通信:去中心化共享及通訊(DSAC)

在当今数字化时代&#xff0c;分布式系统的重要性愈发凸显。它不仅能提升数据的存储安全性和可靠性&#xff0c;还能增强通信的效率和隐私性。于是我做了这个去中心化共享及通訊的程序&#xff0c;它构建了一个强大的分布式存储和通信网络&#xff0c;下面我们就来详细了解其实…

ass字幕嵌入mp4带偏移

# 格式转化文件&#xff0c;包含多种文件的互相转化&#xff0c;主要与视频相关 from pathlib import Path import subprocess import random import os import reclass Utils(object):staticmethoddef get_decimal_part(x: float) -> float:s format(x, .15f) # 格式化为…

05 APP 自动化- Appium 单点触控 多点触控

文章目录 一、单点触控查看指针的指针位置实现手势密码&#xff1a; 二、多点触控 一、单点触控 查看指针的指针位置 方便查看手势密码-九宫格每个点的坐标 实现手势密码&#xff1a; 执行手势操作&#xff1a; 按压起点 -> 移动到下一点 -> 依次移动 -> 释放&am…

【软件】在 macOS 上安装 MySQL

在 macOS 上安装 MySQL 有多种方法&#xff0c;以下是两种常见的安装方式&#xff1a;通过 Homebrew 安装和通过安装包安装。以下是详细的步骤&#xff1a; 一、通过 Homebrew 安装 MySQL Homebrew 是 macOS 的包管理器&#xff0c;使用它安装 MySQL 非常方便。 1.安装 Home…

第11节 Node.js 模块系统

为了让Node.js的文件可以相互调用&#xff0c;Node.js提供了一个简单的模块系统。 模块是Node.js 应用程序的基本组成部分&#xff0c;文件和模块是一一对应的。换言之&#xff0c;一个 Node.js 文件就是一个模块&#xff0c;这个文件可能是JavaScript 代码、JSON 或者编译过的…

力扣热题100之二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 代码 方法&#xff1a;递归 计算二叉树的直径可以理解…

OpenCV CUDA模块图像处理------创建CUDA加速的Canny边缘检测器对象createCannyEdgeDetector()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于创建一个 CUDA 加速的 Canny 边缘检测器对象&#xff08;CannyEdgeDetector&#xff09;&#xff0c;可以在 GPU 上高效执行 Canny 边…

unix/linux,sudo,其内部结构机制

我们现在深入sudo的“引擎室”,探究其内部的结构和运作机制。这就像我们从观察行星运动,到深入研究万有引力定律的数学表达和物理内涵一样,是理解事物本质的关键一步。 sudo 的内部结构与机制详解 sudo 的执行流程可以看作是一系列精心设计的步骤,确保了授权的准确性和安…

什么是 TOML?

&#x1f6e0; Rust 配置文件实战&#xff1a;TOML 语法详解与结构体映射&#xff08; 在 Rust 中&#xff0c;Cargo.toml 是每个项目的心脏。它不仅定义了项目的名称、版本和依赖项&#xff0c;还使用了一种轻巧易读的配置语言&#xff1a;TOML。 本文将深入解析 TOML 的语法…

react native webview加载本地HTML,解决iOS无法加载成功问题

在react native中使用 “react-native-webview”: “^13.13.5”,加载HTML文件 Android: 将HTML文件放置到android/src/main/assets目录&#xff0c;访问 {uri: file:///android_asset/markmap/index.html}ios: 在IOS中可以直接可以直接放在react native项目下&#xff0c;访问…

数据结构(JAVA版)练习题

&#xff08;题目难易程度与题号顺序无关哦&#xff09; 目录 1、多关键字排序 2、集合类的综合应用问题 3、数组排序 4、球的相关计算问题 5、利用类对象计算日期 6、日期计算问题 7、星期日期的计算 8、计算坐标平面上两点距离 9、异常处理设计问题 10、Java源文件…