基于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++协程实现(时间轮定时器)
在后端的开发中,定时器有很广泛的应用。 比如: 心跳检测 倒计时 游戏开发的技能冷却 redis的键值的有效期等等,都会使用到定时器。 定时器的实现数据结构选择 红黑树 对于增删查,时间复杂度为O(logn),对于红黑…...
java多线程与线程池-04线程池与AQS
第7章 线程池与AQS java.util.concurrent包中的绝大多数同步工具,如锁(locks)和屏障(barriers)等,都基于AbstractQueuedSynchronizer(简称AQS)构建而成。这个框架提供了一套同步管理的通用机制,如同步状态的原子性管理、线程阻塞与解除阻塞,还有线程排队等。 在JD…...
优化模型验证关键代码25:样本均值近似技术处理两阶段随机旅行商问题及Gurobipy代码验证
大多数数学规划模型都会考虑到研究问题中存在的不确定性,针对这些不确定性,两种常用的处理方法是鲁棒优化和随机规划。这篇论文我们关注后者,也就是两阶段随机旅行商问题;利用套期保值算法计算不同规模TSP的可行解,同时比较了样本均值近似技术的解的情况,并计算了该问题的…...
老爸:“你做的什么游戏测试简直是不务正业!”——我上去就是一顿猛如虎的解释。
经常有人问我:游戏测试到底是干什么呢?是游戏代练?每天玩游戏?装备随便造,怪物随便秒,线上GM指令随便用?可以每天玩玩游戏,不用忙工作,太爽了?有时朋友不理解…...
JVM垃圾回收调优知识点整理
目录 1、JVM内存模型 1.2、堆及垃圾回收 1.3、JVM参数设置经验: 1.4、对象逃逸分析:...
linux安装mysql-8.0.31
1)、下载mysql-8.0.31压缩包两种方式 a.本地下载后上传服务器解压,下载地址:https://downloads.mysql.com/archives/community/ b.服务器使用命令下载,注意:路径在那,就下载到那个位置。 wget https://dev.mysql.com/…...
2023 年会是网络安全的关键年吗?
过去 12 个月对网络安全领域和周围的每个人来说再次充满挑战。和往年不同,感觉很不一样,攻击源源不断。过去,大型漏洞每季度发生一次,但在过去一年中,在某些情况下,我们几乎每周都会处理严重漏洞。 已知利…...
【深度强化学习】(1) DQN 模型解析,附Pytorch完整代码
大家好,今天和各位讲解一下深度强化学习中的基础模型 DQN,配合 OpenAI 的 gym 环境,训练模型完成一个小游戏,完整代码可以从我的 GitHub 中获得: https://github.com/LiSir-HIT/Reinforcement-Learning/tree/main/Mod…...
Nginx服务优化与防盗链
目录 1.隐藏nginx版本号 1.查看版本号 2.隐藏版本信息 2.修改用户与组 3.缓存时间 4.日志分割 5.连接超时 6.更改进程数 7.网页压缩 8.配置防盗链 1.配置web源主机(192.168.156.10 www.lhf.com) 2.配置域名映射关系 3.配置盗链主机 ࿰…...
npm与yarn常用命令
npm npm -v:查看 npm 版本npm init:初始化后会出现一个 Package.json 配置文件,可以在后面加上 -y,快速跳到问答界面npm install:会根据项目中的 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项目
今日要闻:谷歌发布全球最大视觉语言模型;马斯克预计Twitter下季度现金流转正;王兴投资王慧文ChatGPT项目;美国拟明年 11 月开展载人绕月飞行;慧与科技宣布收购Athonet谷歌发布全球最大视觉语言模型 近日,来…...
java Spring5 xml配置文件方式实现声明式事务
在java Spring5通过声明式事务(注解方式)完成一个简单的事务操作中 我们通过注解方式完成了一个事务操作 那么 下面 我还是讲一下 基于xml实现声明式事务的操作 其实在开发过程中 大家肯定都喜欢用注解 因为他方便 这篇文章中的xml方式 大家做个了解就好 还是 我们的这张表 记…...
常用存储芯片-笔记本上固态硬盘PTS11系列推荐
在存储领域中,除了存储颗粒之外,还有一种极其重要的芯片:存储控制芯片。存储控制芯片是CPU与存储器之间数据交换的中介,决定了存储器最大容量、存取速度等多个重要参数。特别是在AI、5G、自动驾驶时代,对于数据处理及存…...
【AI绘图学习笔记】奇异值分解(SVD)、主成分分析(PCA)
这节的内容需要一些线性代数基础知识,如果你没听懂本文在讲什么,强烈建议你学习【官方双语/合集】线性代数的本质 - 系列合集 文章目录奇异值分解线性变换特征值和特征向量的几何意义什么是奇异值分解?公式推导SVD推广到任意大小矩阵如何求SV…...
【设计模式】模板方法模式和门面模式
模板方法模式和门面模式模板方法模式代码示例门面模式代码示例门面模式的应用场景模板方法模式 模板方法模式非常简单,就是定义了一个固定的公共流程,整个流程有哪些步骤是事先定义好的,具体的步骤则交由子类去实现。属于行为型设计模式。 简…...
Kubernetes未来十年的四大发展趋势
作者:李翔 跟大家已经感受到的一样,Kubernetes已经成为了云计算领域最具统治力的平台,成为了云原生开发的绝对标准,而伴随Kubernetes诞生的CNCF (Cloud Native Computing Foundation) 也因此成为了业界影响力巨大的组织。在成为云…...
一、sql 基础知识、函数和子查询
MySQL 是一种流行的关系型数据库管理系统,使用 SQL 语言进行数据管理和操作。在 MySQL 中,常用的语句包括 SELECT 查询语句、WHERE 条件语句、算术表达式、函数、聚合函数、自定义函数、逻辑表达式、子查询和连接。这些语句可以帮助用户快速地进行数据查…...
产品射频认证笔记
文章目录1. 射频监管认证的目的:1.1 确保 RF 产品在其预期环境中按预期运行1.2 确保射频产品不会干扰其他电子或射频设备2. 射频认证地区规范3. FCC简介4. FCC认证需要准备的内容:5. 射频监管测量会话期间测量以下射频属性:6. 调整射频参数6.…...
链式前向星:高效图存储的进阶指南
1. 为什么需要链式前向星? 当你第一次接触图论算法时,可能会被邻接矩阵和邻接表搞得晕头转向。我刚开始学图论的时候,就经常在这两种存储方式之间纠结。邻接矩阵写起来简单,一个二维数组就能搞定,但当节点数超过10000时…...
Vial-QMK键盘固件从入门到精通:打造专属机械键盘体验
Vial-QMK键盘固件从入门到精通:打造专属机械键盘体验 【免费下载链接】vial-qmk QMK fork with Vial-specific features. 项目地址: https://gitcode.com/gh_mirrors/vi/vial-qmk Vial-QMK是一款功能强大的开源键盘固件,为机械键盘爱好者提供了全…...
解锁智能导航核心:从基础到进阶的路径规划实践指南
解锁智能导航核心:从基础到进阶的路径规划实践指南 【免费下载链接】PathPlanning Common used path planning algorithms with animations. 项目地址: https://gitcode.com/gh_mirrors/pa/PathPlanning 路径规划算法是机器人导航、自动驾驶和游戏AI等领域的…...
从0到1手把手教你搭建AI Agent,打造多智能体协同系统
本文完整展示如何从 0 到 1 手搓一个 AI Agent 的搭建过程。在具体动手实操的过程中,重点为大家展示从需求分析到如何搭建。需求分析中包含如何识别 AI 提效场景和、梳理提效场景流程。如何搭建中包含工作流创建、智能体创建、智能体发布。接下来,将结合…...
OpenClaw极简部署:nanobot镜像+手机Termux方案
OpenClaw极简部署:nanobot镜像手机Termux方案 1. 为什么要在手机上部署OpenClaw? 去年夏天,我在咖啡馆等朋友时突发奇想:如果能用手机随时调用AI助手处理文件该多好。当时尝试了几款云端AI工具,但要么功能受限&#…...
OpenClaw成本优化方案:ollama GLM-4.7-Flash自建模型接口实践
OpenClaw成本优化方案:ollama GLM-4.7-Flash自建模型接口实践 1. 为什么需要关注OpenClaw的token消耗问题 第一次用OpenClaw完成自动化周报任务时,我盯着账单倒吸一口凉气——生成三份周报竟然消耗了接近15万token。这让我意识到,如果不解决…...
swoole方案 WebSocket 下推消息优先级队列
WebSocket 推消息优先级队列 大白话先说清楚 普通弹幕: "哈哈哈哈哈" 优先级 1 (低) 礼物打赏: "送了火箭!" 优先级 2 (中) 系统广播: "服务器维护通知" 优先级 3 (高)队列里同…...
WeMod Pro免费解锁终极指南:两种补丁方法完整对比与实战教程
WeMod Pro免费解锁终极指南:两种补丁方法完整对比与实战教程 【免费下载链接】Wemod-Patcher WeMod patcher allows you to get some WeMod Pro features absolutely free 项目地址: https://gitcode.com/gh_mirrors/we/Wemod-Patcher 还在为WeMod Pro的高级…...
FTDI FT2232H USB转JTAG实战指南:MPSSE配置与多设备调试
1. FT2232H与JTAG基础入门 第一次接触FT2232H这块芯片时,我完全被它的多功能性震惊了。这块小小的USB转接芯片不仅能处理UART通信,还能通过MPSSE引擎模拟JTAG、SPI、I2C等多种协议。对于嵌入式开发者来说,这简直就是调试神器。 FT2232H最吸引…...
Android NDK开发从入门到实战:解锁应用性能的终极武器
引言 在Android应用开发领域,Java和Kotlin凭借其简洁的语法和强大的框架支持,成为了绝大多数开发者的首选。然而,当面对高性能计算、游戏引擎集成、硬件加速访问或核心算法保护等场景时,纯Java层的实现往往显得力不从心。这时&…...
