定时器的实现

一、定时器是什么 ?

  • 定时器是组织大量定时任务的模块。
  • 定时器是项目底层基础的一个模块,很多业务场景中都需要解决一个问题:延时处理某些任务

二、定时器的应用

  • 心跳检测
    • keepalive。
    • 应用层发送心跳包 。
  • 倒计时。
  • 其他需要延时处理的功能。

三、定时器的实现原理

  • 数据结构:
    • 按照先后过期的任务进行排序。
      • 红黑树:nginx、workflow。
      • 最小堆:libevent(最小二叉堆)、libev(最小四叉堆)、libuv(最小四叉堆)、go(最小四叉堆)。任务比较多的时候,最小四叉堆比最小二叉堆性能高 5%
      • 跳表:redis。
    • 按照执行序进行组织
      • 时间轮:skynet、netty、kafka。
  • 驱动方式:拿当前时间跟数据结构中的最小值进行比较,如果当前时间小于最小值,说明所有的任务都没有过期。
    • reactor 网络编程模型 → IO 多路复用 + 非阻塞 IO
    • timerfd:内核提供的一种机制,把延时任务检测视为一个 IO 进行处理,那么就可以把 timerfd 交由 IO 多路复用进行管理
    • usleep 可以用来模拟时间指针移动,配合时间轮进行工作。

四、定时器的具体实现

  • 数据结构 → 红黑树 → multimap(因为过期时间作为 key 会重复)/ set
    • C++ 14 及以上版本实现了等价 key 的概念:只要比较方式一样,就是等价的 key
    • 基类的引用也具备多态性
  • 触发方式的使用:
    • 使用 timerfd 进行触发。
    • 使用 IO 多路复用的最后一个参数进行触发。
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <time.h> // for timespec itimerspec
#include <unistd.h> // for close#include <functional>
#include <chrono>
#include <set>
#include <memory>
#include <iostream>using namespace std;struct TimerNodeBase {time_t expire;uint64_t id; 
};struct TimerNode : public TimerNodeBase {using Callback = std::function<void(const TimerNode &node)>;Callback func;TimerNode(int64_t id, time_t expire, Callback func) : func(func) {this->expire = expire;this->id = id;}
};
/*基类的引用也具备多态性。支持 TimerNode 与 TimerNode 比较、TimerNode 与 TimerNodeBase 比较、TimerNodeBase 与 TimerNode 比较、TimerNodeBase 与 TimerNodeBase 比较
*/
bool operator < (const TimerNodeBase &lhd, const TimerNodeBase &rhd) {if (lhd.expire < rhd.expire) {return true;} else if (lhd.expire > rhd.expire) {return false;} else return lhd.id < rhd.id;
}class Timer {
public:static inline time_t GetTick() {return chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count();}/*1. std::bind()2. class operator()3. []() {}*/TimerNodeBase AddTimer(int msec, TimerNode::Callback func) {time_t expire = GetTick() + msec;if (timeouts.empty() || expire <= timeouts.crbegin()->expire) {// emplace 允许直接在集合中构造元素,避免了对象的拷贝auto pairs = timeouts.emplace(GenID(), expire, std::move(func));return static_cast<TimerNodeBase>(*pairs.first);}// 大多数时候 expire 是相同的,将 O(logn) 优化为 O(1) -> 总是往红黑树最右侧增加节点auto ele = timeouts.emplace_hint(timeouts.crbegin().base(), GenID(), expire, std::move(func));return static_cast<TimerNodeBase>(*ele);}void DelTimer(TimerNodeBase &node) {auto iter = timeouts.find(node);if (iter != timeouts.end())timeouts.erase(iter);}void HandleTimer(time_t now) {auto iter = timeouts.begin();while (iter != timeouts.end() && iter->expire <= now) {iter->func(*iter);iter = timeouts.erase(iter);}}public:virtual 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);}private:static inline uint64_t GenID() {return gid++;}static uint64_t gid; set<TimerNode, std::less<>> timeouts;
};
uint64_t Timer::gid = 0;int main() {int epfd = epoll_create(1);int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);struct epoll_event ev = {.events=EPOLLIN | EPOLLET};epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &ev);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;struct epoll_event evs[64] = {0};while (true) {timer->UpdateTimerfd(timerfd);int n = epoll_wait(epfd, evs, 64, -1);time_t now = Timer::GetTick();for (int i = 0; i < n; i++) {// for network event handle}timer->HandleTimer(now);}epoll_ctl(epfd, EPOLL_CTL_DEL, timerfd, &ev);close(timerfd);close(epfd);return 0;
}

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

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

相关文章

大型连锁企业异地组网稳定性提升指南

随着时代的发展&#xff0c;连锁企业在网络方面面临着越来越多的挑战。这些企业在不同的地理位置设有分支机构和门店&#xff0c;可能是同城也可能是异地&#xff0c;因此需要确保各个地点之间的网络连接稳定可靠。然而&#xff0c;由于不同地区网络基础设施、延迟和带宽等方面…

sort和sorted的区别使用

llist(map(int,input().split())) bsorted(l,reverseFalse)#reverseTrue是降序&#xff0c;False是降序 print(b) #sorted&#xff08;&#xff09;函数是将一个排好序的列表赋给另一变量 a.sort(reverseFalse)#用法和sorted一样 #只不过sort函数直接将列表进行排序不能赋给其他…

代码随想录:栈与队列4-6

20.有效的括号 题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一…

打卡--MySQL8.0 四 (索引及执行计划)

一、索引的创建与使用 1、索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 &#xff08;1&#xff09;从 功能逻辑 上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 &#xff08;…

【测试开发学习历程】python迭代、可迭代对象、迭代器、生成器

1 迭代Iteration 迭代Iteration&#xff1a;所谓迭代就是重复运行一段代码语句块的能力&#xff0c;就好比在一个容器中进行一层一层遍历数据&#xff0c;在应用过程中for循环最为突出。迭代就是从某个容器对象中逐个地读取元素&#xff0c;直到容器中没有元素为止。迭代迭代&…

maven: 标签总结

文章目录 一、scope二、type 一、scope 控制依赖项不同阶段的可见性和使用范围。 <!-- SpringBoot 依赖配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version…

【动态规划-线性dp】【蓝桥杯备考训练】:乌龟棋、最长上升子序列、最长公共子序列、松散子序列、最大上升子序列和【已更新完成】

目录 1、乌龟棋 2、最长上升子序列 3、最长公共子序列 4、松散子序列 5、最大上升子序列和 1、乌龟棋 小明过生日的时候&#xff0c;爸爸送给他一副乌龟棋当作礼物。 乌龟棋的棋盘只有一行&#xff0c;该行有 N 个格子&#xff0c;每个格子上一个分数&#xff08;非负整数&am…

【R基础】一组数据计算均值、方差与标准差方法及意义

【R基础】一组数据计算均值、方差与标准差方法及意义 均值、方差与标准差是用来描述数据分布情况 均值&#xff1a;用来衡量一组数据整体情况。 数据离散程度度量标准&#xff1a; 方差&#xff08;均方&#xff0c;s^2&#xff0c;总体参数&#xff0c;离均差平方和&#…

AI大模型原理科普(深度好文)

目录 认识AI大模型家族 AI是什么&#xff1f; 机器学习是什么&#xff1f; 机器学习有哪些分支&#xff1f; 什么是强化学习&#xff1f; 深度学习属于哪一类学习&#xff1f; 生成式AI和深度学习是什么关系&#xff1f; 大语言模型是什么&#xff1f; 所有大语言模型…

【Java】HashMap 源码阅读

HashMap 源码阅读 HashMap 简介 HashMap 主要用来存放键值对&#xff0c;实现了基于哈希表的 Map 接口&#xff0c;非线程安全。 HashMap 可以存放 null 的 key 和 null 值&#xff0c;但 null 作为 key 只能有一个&#xff0c;null 作为 value 可以有多个。 HashMap 有两个…

靡语IT:Bootstrap 简介

1.1 Bootstrap 简介&#xff1a;什么是 Bootstrap? Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架。Bootstrap是前端开发中比较受欢迎的框架&#xff0c;简洁且灵活。它基于HTML、CSS和JavaScript&#xff0c;HTML定义页面元素&#xff0c;CSS定义页面布局&…

BFS广度优先搜索

import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);//在此输入您…

业务逻辑漏洞(靶场) fiddler

目录 fiddler简介&#xff1a; 业务逻辑漏洞&#xff1a; fiddler下载 靶场&#xff1a; 实验一 ​编辑实验二&#xff08;ps 更改实验url会变&#xff0c;fiddler没抓到东西看看代理改没改&#xff09; 实验三 实验四 fiddler简介&#xff1a; 一款网络抓包工具&#…

内部类、泛型、常用API

内部类 内部类是类中的五大成分之一&#xff08;成员变量、方法、构造器、代码块、内部类&#xff09; 如果一个类定义在另一个类的内部&#xff0c;这个类就是内部类。 场景&#xff1a;当一个类的内部&#xff0c;包含了一个完整的事物&#xff0c;且这个事物没有必要单独设…

20240403-算法复习打卡day43||● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

1049. 最后一块石头的重量 II class Solution { public:int lastStoneWeightII(vector<int>& stones) {vector<int> dp(15001, 0);int sum 0;for (int i 0; i < stones.size(); i) sum stones[i];int target sum / 2;for (int i 0; i < stones.siz…

智慧城市中的物联网革命——青创智通

工业物联网解决方案-工业IOT-青创智通 得益于物联网 (IoT)的变革力量&#xff0c;智慧城市的概念正在迅速成为现实。物联网正在从根本上改变城市的运作方式&#xff0c;为城市居民带来更高的效率、可持续性和生活质量。在本文中&#xff0c;我们将探讨物联网在智慧城市中的作用…

49 样式迁移【李沐动手学深度学习v2课程笔记】

1. 样式迁移&#xff08;Style Transfer) 计算机视觉的应用之一&#xff0c;将样式图片中的样式&#xff08;比如油画风格等&#xff09;迁移到内容图片&#xff08;比如实拍的图片&#xff09;上&#xff0c;得到合成图片 可以理解成为一个滤镜&#xff0c;但相对于滤镜来讲…

SAP ABAP CDS-02 ABAP CDS语法

ABAP CDS中的CDS DDL和CDS DCL的语法包括一般SQL DDL和DCL的元素&#xff0c;还可以定义注释和CDS关联。其语法和语义基本符合CDS的一般概念。 ABAP CDS - 一般语法规则&#xff08;General Syntax Rules&#xff09; 在ABAP CDS中定义CDS对象的CDS DDL和CDS DCL的一般语法规…

基于SSM框架JAVA仓库管理系统源代码Mysql数据库(可当毕设,实训项目,设计大赛)

仓库管理系统实现的功能包括店铺管理&#xff0c;员工管理&#xff0c;部门管理&#xff0c;商品管理&#xff0c;权限管理&#xff0c;入库管理&#xff0c;出库管理&#xff0c;盘点管理&#xff0c;统计管理等功能。该项目采用了Mysql数据库&#xff0c;Java语言&#xff0c…

Hibernate多事务同时调用update(T t) ,字段被覆盖问题

前言 今天现网有个订单卡单了&#xff0c;经过排查发现没有任何异常日志&#xff0c;根据日志定位发现本应该更新的一个状态&#xff0c;也sql肯定执行了(使用了Hibernate的ORM框架)&#xff0c;但是数据库里面的状态没有更新。大概逻辑如下 String hql from orderInfo where…