当前位置: 首页 > news >正文

基于libco的c++协程实现(时间轮定时器)

在后端的开发中,定时器有很广泛的应用。

比如:

心跳检测

倒计时

游戏开发的技能冷却

redis的键值的有效期等等,都会使用到定时器。

定时器的实现数据结构选择

红黑树

对于增删查,时间复杂度为O(logn),对于红黑树最⼩节点为最左侧节点,时间复杂度O(logn)

最小堆

对于增查,时间复杂度为O(logn),对于删时间复杂度为O(n),但是可以通过辅助数据结构( map 或者hashtable来快速索引节点)来加快删除操作;对于最⼩节点为根节点,时间复杂度为O(1)

跳表

对于增删查,时间复杂度为O(logn),对于跳表最⼩节点为最左侧节点,时间复杂度为O(1),但是空间复杂度⽐较⾼,为O(1.5n)

时间轮

对于增删查,时间复杂度为O(1),查找最⼩节点也为O(1)

libco的使用了时间轮的实现

首先,时间轮有几个结构,必须理清他们的关系。


struct stTimeoutItem_t
{enum { eMaxTimeout = 40 * 1000 };	// 40sstTimeoutItem_t* pPrev;				// 前stTimeoutItem_t* pNext;				// 后stTimeoutItemLink_t* pLink;			// 链表,没有用到,写这里有毛用OnPreparePfn_t pfnPrepare;			// 不是超时的事件的处理函数OnProcessPfn_t pfnProcess;			// resume协程回调函数void* pArg;							// routine 协程对象指针bool bTimeout;						// 是否超时unsigned long long ullExpireTime;	// 到期时间
};struct stPoll_t;
struct stPollItem_t : public stTimeoutItem_t
{struct pollfd* pSelf;			// 对应的poll结构stPoll_t* pPoll;				// 所属的stPoll_tstruct epoll_event stEvent;		// epoll事件,poll转换过来的
};// co_poll_inner 创建,管理这多个stPollItem_t
struct stPoll_t : public stTimeoutItem_t
{struct pollfd* fds;				// poll 的fd集合nfds_t nfds;					// poll 事件个数stPollItem_t* pPollItems;		// 要加入epoll 事件int iAllEventDetach;			// 如果处理过该对象的子项目pPollItems,赋值为1int iEpollFd;					// epoll fd句柄int iRaiseCnt;					// 此次触发的事件数
};

我把这几个结构拉一起了,

 其中,能看出,stCoEpool_t管理了这一切


// TimeoutItem的链表
struct stTimeoutItemLink_t
{stTimeoutItem_t* head;stTimeoutItem_t* tail;
};// TimeOut 
struct stTimeout_t	// 时间伦
{stTimeoutItemLink_t* pItems;	// 时间轮链表,开始初始化分配只一圈的长度,后续直接使用int iItemSize;					// 超时链表中一圈的tick 60*1000unsigned long long ullStart;	// 时间轮开始时间,会一直变化long long llStartIdx;			// 时间轮开始的下标,会一直变化
};// epoll 结构
struct stCoEpoll_t
{int iEpollFd;static const int _EPOLL_SIZE = 1024 * 10;struct stTimeout_t* pTimeout;					// epoll 存着时间轮struct stTimeoutItemLink_t* pstTimeoutList;		// 超时事件链表struct stTimeoutItemLink_t* pstActiveList;		// 用于signal时会插入co_epoll_res* result;
};

也就是说,一个协程,就有一个,在co_init_curr_thread_env 中创建

它管理着超时链表,信号事件链表

其中的pTimeout,就是时间轮,也就是一个数组,这个数组的大小位60*1000

 

stTimeout_t *AllocTimeout( int iSize )
{stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) );lp->iItemSize = iSize;// 注意这里先把item分配好了,后续直接使用lp->pItems = (stTimeoutItemLink_t*)calloc( 1, sizeof(stTimeoutItemLink_t) * lp->iItemSize );lp->ullStart = GetTickMS();lp->llStartIdx = 0;return lp;
}

这就是分配的时间轮的方法,首先指定了下标时间等信息,根据结构注释应该不难懂

相关视频推荐

红黑树、最小堆、时间轮、跳表多种方式实现定时器

海量定时任务设计-时间轮

2023年最新技术图谱,c++后端的8个技术维度,助力你快速成为大牛

免费学习地址:c/c++ linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

 ​有了这些后,再来看看时怎么添加超时事件的

// apTimeout:时间轮
// apItem: 某一个定时item
// allNow:当前的时间
// 函数目的,将超时项apItem加入到apTimeout
int AddTimeout( stTimeout_t *apTimeout, stTimeoutItem_t *apItem ,unsigned long long allNow )
{// 这个判断有点多余,start正常已经分配了if( apTimeout->ullStart == 0 ){apTimeout->ullStart = allNow;apTimeout->llStartIdx = 0;}// 当前时间也不大可能比前面的时间大if( allNow < apTimeout->ullStart ){co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu",__LINE__,allNow,apTimeout->ullStart);return __LINE__;}if( apItem->ullExpireTime < allNow ){co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu",__LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart);return __LINE__;}// 到期时间到start的时间差unsigned long long diff = apItem->ullExpireTime - apTimeout->ullStart;// itemsize 实际上是毫秒数,如果超出了,说明设置的超时时间过长if( diff >= (unsigned long long)apTimeout->iItemSize ){diff = apTimeout->iItemSize - 1;co_log_err("CO_ERR: AddTimeout line %d diff %d",__LINE__,diff);//return __LINE__;}// 将apItem加到末尾AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem );return 0;
}

其实,这里有个概念,stTimeoutItemLink_t 与stTimeoutItem_t,也就是说,stTimeout_t里面管理的时60*1000个链表,而每个链表有一个或者多个stTimeoutItem_t,下面这个函数,就是把节点Item加入到链表的方法。


template <class TNode,class TLink>
void inline AddTail(TLink*apLink, TNode *ap)
{if( ap->pLink ){return ;}if(apLink->tail){apLink->tail->pNext = (TNode*)ap;ap->pNext = NULL;ap->pPrev = apLink->tail;apLink->tail = ap;}else{apLink->head = apLink->tail = ap;ap->pNext = ap->pPrev = NULL;}ap->pLink = apLink;
}

 ​到这里,基本把一个超时事件添加到时间轮中了,这时就应该切换协程了co_yield_env

	int ret = AddTimeout( ctx->pTimeout, &arg, now );int iRaiseCnt = 0;if( ret != 0 ){co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld",ret,now,timeout,arg.ullExpireTime);errno = EINVAL;iRaiseCnt = -1;}else{co_yield_env( co_get_curr_thread_env() );iRaiseCnt = arg.iRaiseCnt;}

接下来,看怎么检测超时事件co_eventloop

    for(;;){// 等待事件或超时1msint ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 );//  遍历所有ret事件处理for(int i=0;i<ret;i++){pfnPrepare(xxx)}// 取出所有的超时时间item,设置为超时TakeAllTimeout( ctx->pTimeout, now, plsTimeout );stTimeoutItem_t *lp = plsTimeout->head;while( lp ){lp->bTimeout = true;lp = lp->pNext;}// 将超时链表plsTimeout加入到plsActiveJoin<stTimeoutItem_t, stTimeoutItemLink_t>( plsActive, plsTimeout );lp = plsActive->head;while( lp ){// 弹出链表头,处理超时事件PopHead<stTimeoutItem_t,stTimeoutItemLink_t>( plsActive );if (lp->bTimeout && now < lp->ullExpireTime) {int ret = AddTimeout(ctx->pTimeout, lp, now);if (!ret) {lp->bTimeout = false;lp = plsActive->head;continue;}}// 只有stPool_t 才需要切协程,要切回去了if( lp->pfnProcess ){lp->pfnProcess( lp );}lp = plsActive->head;}// 如果传入该函数指针,则可以控制event_loop 退出if( pfn ){if( -1 == pfn( arg ) ){break;}}}

其中包括了定时事件处理,协程切换,主协程退出等操作。如果设置了主协程退出函数,则主协程可以正常的退出。

相关文章:

基于libco的c++协程实现(时间轮定时器)

在后端的开发中&#xff0c;定时器有很广泛的应用。 比如&#xff1a; 心跳检测 倒计时 游戏开发的技能冷却 redis的键值的有效期等等&#xff0c;都会使用到定时器。 定时器的实现数据结构选择 红黑树 对于增删查&#xff0c;时间复杂度为O(logn)&#xff0c;对于红黑…...

java多线程与线程池-04线程池与AQS

第7章 线程池与AQS java.util.concurrent包中的绝大多数同步工具,如锁(locks)和屏障(barriers)等,都基于AbstractQueuedSynchronizer(简称AQS)构建而成。这个框架提供了一套同步管理的通用机制,如同步状态的原子性管理、线程阻塞与解除阻塞,还有线程排队等。 在JD…...

优化模型验证关键代码25:样本均值近似技术处理两阶段随机旅行商问题及Gurobipy代码验证

大多数数学规划模型都会考虑到研究问题中存在的不确定性,针对这些不确定性,两种常用的处理方法是鲁棒优化和随机规划。这篇论文我们关注后者,也就是两阶段随机旅行商问题;利用套期保值算法计算不同规模TSP的可行解,同时比较了样本均值近似技术的解的情况,并计算了该问题的…...

老爸:“你做的什么游戏测试简直是不务正业!”——我上去就是一顿猛如虎的解释。

经常有人问我&#xff1a;游戏测试到底是干什么呢&#xff1f;是游戏代练&#xff1f;每天玩游戏&#xff1f;装备随便造&#xff0c;怪物随便秒&#xff0c;线上GM指令随便用&#xff1f;可以每天玩玩游戏&#xff0c;不用忙工作&#xff0c;太爽了&#xff1f;有时朋友不理解…...

JVM垃圾回收调优知识点整理

目录 1、JVM内存模型 1.2、堆及垃圾回收 1.3、JVM参数设置经验: 1.4、对象逃逸分析:...

linux安装mysql-8.0.31

1)、下载mysql-8.0.31压缩包两种方式 a.本地下载后上传服务器解压&#xff0c;下载地址&#xff1a;https://downloads.mysql.com/archives/community/ b.服务器使用命令下载&#xff0c;注意&#xff1a;路径在那&#xff0c;就下载到那个位置。 wget https://dev.mysql.com/…...

2023 年会是网络安全的关键年吗?

过去 12 个月对网络安全领域和周围的每个人来说再次充满挑战。和往年不同&#xff0c;感觉很不一样&#xff0c;攻击源源不断。过去&#xff0c;大型漏洞每季度发生一次&#xff0c;但在过去一年中&#xff0c;在某些情况下&#xff0c;我们几乎每周都会处理严重漏洞。 已知利…...

【深度强化学习】(1) DQN 模型解析,附Pytorch完整代码

大家好&#xff0c;今天和各位讲解一下深度强化学习中的基础模型 DQN&#xff0c;配合 OpenAI 的 gym 环境&#xff0c;训练模型完成一个小游戏&#xff0c;完整代码可以从我的 GitHub 中获得&#xff1a; https://github.com/LiSir-HIT/Reinforcement-Learning/tree/main/Mod…...

Nginx服务优化与防盗链

目录 1.隐藏nginx版本号 1.查看版本号 2.隐藏版本信息 2.修改用户与组 3.缓存时间 4.日志分割 5.连接超时 6.更改进程数 7.网页压缩 8.配置防盗链 1.配置web源主机&#xff08;192.168.156.10 www.lhf.com&#xff09; 2.配置域名映射关系 3.配置盗链主机 &#xff0…...

npm与yarn常用命令

npm npm -v&#xff1a;查看 npm 版本npm init&#xff1a;初始化后会出现一个 Package.json 配置文件&#xff0c;可以在后面加上 -y&#xff0c;快速跳到问答界面npm install&#xff1a;会根据项目中的 package.json 文件自动给下载项目中所需的全部依赖npm insall 包含 --…...

【C++】C++11新特性——右值引用

文章目录一、左值引用、 右值引用1.1 左值与右值1.2 左值引用1.3 右值引用二、右值引用的意义三、移动语句3.1 移动构造3.2 移动赋值3.3 总结四、move问题五、完美转发5.1 万能引用与折叠5.2 完美转发std::forward一、左值引用、 右值引用 1.1 左值与右值 我们经常能听到左值…...

C#基础教程21 正则表达式

文章目录 简介正则表达式语法字符集元字符转义字符量词贪婪匹配和非贪婪匹配正则表达式类Regex类Match方法Matches方法简介 正则表达式是一种描述字符串模式的语言,它可以用来匹配、查找、替换字符串中的模式。在C#中,我们可以使用System.Text.RegularExpressions命名空间下的…...

聚观早报|谷歌发布最大视觉语言模型;王兴投资王慧文ChatGPT项目

今日要闻&#xff1a;谷歌发布全球最大视觉语言模型&#xff1b;马斯克预计Twitter下季度现金流转正&#xff1b;王兴投资王慧文ChatGPT项目&#xff1b;美国拟明年 11 月开展载人绕月飞行&#xff1b;慧与科技宣布收购Athonet谷歌发布全球最大视觉语言模型 近日&#xff0c;来…...

java Spring5 xml配置文件方式实现声明式事务

在java Spring5通过声明式事务(注解方式)完成一个简单的事务操作中 我们通过注解方式完成了一个事务操作 那么 下面 我还是讲一下 基于xml实现声明式事务的操作 其实在开发过程中 大家肯定都喜欢用注解 因为他方便 这篇文章中的xml方式 大家做个了解就好 还是 我们的这张表 记…...

常用存储芯片-笔记本上固态硬盘PTS11系列推荐

在存储领域中&#xff0c;除了存储颗粒之外&#xff0c;还有一种极其重要的芯片&#xff1a;存储控制芯片。存储控制芯片是CPU与存储器之间数据交换的中介&#xff0c;决定了存储器最大容量、存取速度等多个重要参数。特别是在AI、5G、自动驾驶时代&#xff0c;对于数据处理及存…...

【AI绘图学习笔记】奇异值分解(SVD)、主成分分析(PCA)

这节的内容需要一些线性代数基础知识&#xff0c;如果你没听懂本文在讲什么&#xff0c;强烈建议你学习【官方双语/合集】线性代数的本质 - 系列合集 文章目录奇异值分解线性变换特征值和特征向量的几何意义什么是奇异值分解&#xff1f;公式推导SVD推广到任意大小矩阵如何求SV…...

【设计模式】模板方法模式和门面模式

模板方法模式和门面模式模板方法模式代码示例门面模式代码示例门面模式的应用场景模板方法模式 模板方法模式非常简单&#xff0c;就是定义了一个固定的公共流程&#xff0c;整个流程有哪些步骤是事先定义好的&#xff0c;具体的步骤则交由子类去实现。属于行为型设计模式。 简…...

Kubernetes未来十年的四大发展趋势

作者&#xff1a;李翔 跟大家已经感受到的一样&#xff0c;Kubernetes已经成为了云计算领域最具统治力的平台&#xff0c;成为了云原生开发的绝对标准&#xff0c;而伴随Kubernetes诞生的CNCF (Cloud Native Computing Foundation) 也因此成为了业界影响力巨大的组织。在成为云…...

一、sql 基础知识、函数和子查询

MySQL 是一种流行的关系型数据库管理系统&#xff0c;使用 SQL 语言进行数据管理和操作。在 MySQL 中&#xff0c;常用的语句包括 SELECT 查询语句、WHERE 条件语句、算术表达式、函数、聚合函数、自定义函数、逻辑表达式、子查询和连接。这些语句可以帮助用户快速地进行数据查…...

产品射频认证笔记

文章目录1. 射频监管认证的目的&#xff1a;1.1 确保 RF 产品在其预期环境中按预期运行1.2 确保射频产品不会干扰其他电子或射频设备2. 射频认证地区规范3. FCC简介4. FCC认证需要准备的内容&#xff1a;5. 射频监管测量会话期间测量以下射频属性&#xff1a;6. 调整射频参数6.…...

做了个springboot接口参数解密的工具,我给它命名为万能钥匙(已上传maven中央仓库,附详细使用说明)

前言&#xff1a;之前工作中做过两个功能&#xff0c;就是之前写的这两篇博客&#xff0c;最近几天有个想法&#xff0c;给它做成一个springboot的start启动器&#xff0c;直接引入依赖&#xff0c;写好配置就能用了 springboot使用自定义注解实现接口参数解密&#xff0c;普通…...

【Flutter从入门到入坑】Flutter 知识体系

学习 Flutter 需要掌握哪些知识&#xff1f; 终端设备越来越碎片化&#xff0c;需要支持的操作系统越来越多&#xff0c;从研发效率和维护成本综合考虑&#xff0c;跨平台开发一定是未来大前端的趋势&#xff0c;我们应该拥抱变化。而 Flutter 提供了一套彻底的移动跨平台方案…...

顺序表的基本操作

目录 一.什么是顺序表 二.顺序表的基本操作 1.初始化 2.增容 3.尾插 4.头插 5.尾删 6.头删 7.指定位置插入 8.指定位置删除 9.打印 10.查找 11.销毁 一.什么是顺序表 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组…...

设计模式——创建型模型——单列模式(8种实现)

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;计算机基础专栏 &#x1f4e7;如果文章知识点有错误的地方&#…...

【软考中级】软件设计师笔记

计算机系统的性能一般包括两个方面&#xff1a;一方面是它的可用性&#xff0c;也就是计算机系统能正常工作的时间&#xff0c;其指标可以是能够持续工作的时间长度&#xff0c;也可以是在一段时间内&#xff0c;能正常工作的时间所占的百分比 另一方面是处理能力&#xff0c;又…...

包教包会的ES6

自学参考&#xff1a;http://es6.ruanyifeng.com/ 一、ECMAScript 6 简介 ECMAScript 6.0&#xff08;以下简称 ES6&#xff09;是 JavaScript 语言的下一代标准&#xff0c;已经在 2015 年 6 月正式发布了。它的目标&#xff0c;是使得 JavaScript 语言可以用来编写复杂的大…...

python学习——【第四弹】

前言 上一篇文章 python学习——【第三弹】 中学习了python中的流程控制语句&#xff0c;这篇文章我们接着学习python中的序列。先给大家介绍不可变序列 字符串和可变序列 列表&#xff0c;下一篇文章接着补充元组&#xff0c;集合和字典。 序列 指的是一块可以存放多个值的…...

Web3中文|无聊猿Otherside元宇宙启动第二次旅行

3月9日消息&#xff0c;无聊猿Bored Ape Yacht Club母公司Yuga Labs公布了其Otherside元宇宙游戏平台第二次测试的最新细节。Yuga Labs公司称&#xff0c;“第二次旅行”将于3月25日举行&#xff0c;由四位Otherside团队长带领完成近两小时的游戏故事。本次旅行对Otherdeed NFT…...

SpringCloud-7_OpenFeign服务调用

OpenFeign介绍OpenFeign是什么1.OpenFeign是个声明式WebService客户端&#xff0c;使用OpenFeign让编写Web Service客户端更简单2.它的使用方法是定义一个服务接口然后在上面添加注解3.OpenFeign也支持可拔插式的编码器和解码器4.Spring Cloud对OpenFeign进行了封装使其支持了S…...

解决docker容器之间网络互通

docker容器之间相互访问 1.查看当前的网络 Copy [roothost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 3dd4643bb158 bridge bridge local 748b765aca52 host host …...

嘉兴英文网站建设/营销型网站和普通网站

内部碎片的产生&#xff1a;因为所有的内存分配必须起始于可被 4、8 或 16 整除&#xff08;视处理器体系结构而定&#xff09;的地址或者因为MMU的分页机制的限制&#xff0c;决定内存分配算法仅能把预定大小的内存块分配给客户。假设当某个客户请求一个43字节的内存块时&…...

做住宿的网站/iis7站长工具

1、布署ApplicationErrorLog未处理异常处理组件。A、引用相关组件。B、修改Web.config<?xml version"1.0"?><configuration><configSections><section name"ApplicationErrorLogData"type"Lion.Web.ApplicationErrorLog.Confi…...

html5微网站/没有限制的国外搜索引擎

1. 什么是内连接、外连接、交叉连接、笛卡尔积呢&#xff1f; 内连接&#xff08;inner join&#xff09;&#xff1a;取得两张表中满足存在连接匹配关系的记录。 外连接&#xff08;outer join&#xff09;&#xff1a;不只取得两张表中满足存在连接匹配关系的记录&#xff0…...

无锡高端网站建设公司哪家好/seo计费系统

1. 在"The system is running in low-graphics mode"界面&#xff0c;直接按 ctrlaltF1&#xff0c;会进入一个命令输入的模式 2. 输入用户名密码&#xff08;这里我是root 加密码&#xff09; 3. 输入以下命令: cd /etc/X11 &#xff08;X大写&#xff09; sudo cp…...

室内设计软件大全网站/百度广告怎么收费

mate: 伙伴 matey: 融洽的, 易于亲近的. get matey with sb. poison: a. 有毒的, n.毒药/毒酒v. 下毒, 破坏, 污染 slander [ 撕烂的~~], n. 中伤,诽谤, v. 诽谤 slanderous: 中伤的, 诽谤的. this is pure slander . the slander poisoned his mind. college : ege 相对开音节…...

ps做网站首页/链接买卖价格

OC点语法和变量作用域 一、点语法 &#xff08;一&#xff09;认识点语法 声明一个Person类: 1 #import <Foundation/Foundation.h>2 3 interface Person : NSObject4 {5 int _age;//默认为protected 6 }7 8 - (void)setAge:(int)age;9 - (int)age; 10 11 end Person类…...