Muduo 定时器

article/2025/11/6 18:52:06

TimeQueue定时器

图片转载自:muduo网络库源码解析(4):TimerQueue定时机制_李兆龙的技术博客_51CTO博客

muduo网络库源码解析(4):TimerQueue定时机制_事件分发机制

添加新的定时器

TimerId TimerQueue::addTimer(TimerCallback cb,	//用户自定义回调Timestamp when,	  //定时器的超时时刻double interval)	  //重复触发间隔,小于0则不重复触发
{Timer* timer = new Timer(std::move(cb), when, interval);	//回调函数交由timer保管loop_->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));return TimerId(timer, timer->sequence());
}

通过addTImer添加一个定时器(实际调用的是addTimerInLoop),在runInLoop中注册一个函数addTimerInLoop,

之后在EventLoop的runInLoop中会把该函数放入pedingFunctor中,在loop中调用执行.

当然runInloop并不只有这么简单.

删除定时器

void TimerQueue::cancel(TimerId timerId)
{loop_->runInLoop(std::bind(&TimerQueue::cancelInLoop, this, timerId));
}

也是一样把实际做删除操作的函数放入pedingFunctor,在loop中调用执行

TimerQueue::TimerQueue(EventLoop* loop): loop_(loop),	//当前TimeQueue属于某个EventLooptimerfd_(createTimerfd()),	//初始化一个timefdtimerfdChannel_(loop, timerfd_),	//初始化一个关注timerfd的channeltimers_(),	//保存所有的定时器callingExpiredTimers_(false)
{timerfdChannel_.setReadCallback(std::bind(&TimerQueue::handleRead, this));	//为timerfd注册读回调// we are always reading the timerfd, we disarm it with timerfd_settime.timerfdChannel_.enableReading();	//使timerfd读可用
}

在构造函数中有一个timerfd,这是一个文件描述符,当它被设置的超时时间到了后变为可读,所以它可以被pool/epoll监听.

设置了超时后调用的回调函数 handleRead

timerfd可读后触发的回调函数

void TimerQueue::handleRead()
{loop_->assertInLoopThread();Timestamp now(Timestamp::now());//获取当前时间readTimerfd(timerfd_, now);	//读取超时时间,因为muduo默认LT 防止多次触发std::vector<Entry> expired = getExpired(now);	//获取目前超时的timer,并且移动给expiredcallingExpiredTimers_ = true;	//处理超时事件/*在处理超时事件时,如果超时的timer被删除了(也就是调用了cancelInLoop),那么为了保证超时回调正确执行,会把它加入到待删除队列中,再在reset()函数中对待删除队列执行操作(cancelingTimers_)*/cancelingTimers_.clear();	//清空删除队列,这里面的timer已经在reset中被delete了// safe to callback outside critical sectionfor (const Entry& it : expired){it.second->run();		//通过timer调用用户传入的超时回调}callingExpiredTimers_ = false;	//超时事件处理完成reset(expired, now);
}

在handleRead()中,通过getExpired()读取到了所有超时的定时器并返回vector维护,之后依次处理这些超时的定时器,并且调用这些超时定时器的回调

可以看到 callingExpiredTimers_ 在处理超时事时被设置为了 true 处理完成后则设置为 false

因为在处理超时事件时,定时器已经被移动到了expired中,如果此时在对其进行删除,在timers_和activeTimers_中
是找不到的,所以通过cancelingTimers_保存这些定时器,等到超时事件处理完成再去reset()中处理.

取消定时器实际调用cancelInLoop

void TimerQueue::cancelInLoop(TimerId timerId)
{loop_->assertInLoopThread();assert(timers_.size() == activeTimers_.size());ActiveTimer timer(timerId.timer_, timerId.sequence_);ActiveTimerSet::iterator it = activeTimers_.find(timer);if (it != activeTimers_.end()){size_t n = timers_.erase(Entry(it->first->expiration(), it->first));	//在timers_中删除定时器assert(n == 1); (void)n;delete it->first;  /*在activeTimers_中删除定时器(应为是裸指针,所以delete,并且timers_和activeTimers_中timer_是同一个指针)*/activeTimers_.erase(it);}else if (callingExpiredTimers_)	//判断是否正在处理超时事件{cancelingTimers_.insert(timer);		}assert(timers_.size() == activeTimers_.size());		
}

添加定时器的实际调用

void TimerQueue::addTimerInLoop(Timer* timer)
{loop_->assertInLoopThread();		//判断是否在IO线程中bool earliestChanged = insert(timer);	//插入Timers_和activeTimers_(cancel)if (earliestChanged){//如果timers中最低的时间限度被更新,就更新一次超时时刻resetTimerfd(timerfd_, timer->expiration());}
}
bool TimerQueue::insert(Timer* timer)
{loop_->assertInLoopThread();assert(timers_.size() == activeTimers_.size());bool earliestChanged = false;Timestamp when = timer->expiration();		//获取传入的timer的超时时刻/*获取队列中定时时间最短的项,即第一个 因为数据结构是set,红黑树有序,比较顺序为pair的比较顺序 即先比较first,相同比较second*/TimerList::iterator it = timers_.begin();if (it == timers_.end() || when < it->first)//timers中不存在任何的Timer或者传入的timer超时时刻小于最小的那一个{earliestChanged = true;	//刚刚插入的timer是定时器队列中的超时时刻最短时间内被触发的}{std::pair<TimerList::iterator, bool> result= timers_.insert(Entry(when, timer));//插入set容器, when:超时时间,  timer:定时器//就算Timestamp(超时时间)一样后面的地址(timer,这是一个指针)也一定不一样assert(result.second); (void)result;	//断言,永真}{//插入activeTimers_容器std::pair<ActiveTimerSet::iterator, bool> result= activeTimers_.insert(ActiveTimer(timer, timer->sequence()));assert(result.second); (void)result;}assert(timers_.size() == activeTimers_.size());	//两个容器中的定时器数量要一致return earliestChanged;	//是否把timer插入到set中
}

获取超时定时器getExpired()

std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{assert(timers_.size() == activeTimers_.size());std::vector<Entry> expired;//UINTPTR_MAX为 uintptr_t 类型对象的最大值//“reinterpret_cast 运算符并不会改变括号中运算对象的值,而是对该对象从位模式上进行重新解释”Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));// 返回第一个未到期的Timer的迭代器,因为set是有序的,所以在它前面的tiemr都是到期的// lower_bound的含义是返回第一个值>=sentry的元素的iterator// 即*end >= sentry,从而end->first > now// 注意:此处是>,而不是>=TimerList::iterator end = timers_.lower_bound(sentry);	assert(end == timers_.end() || now < end->first);//将[being(),end)之间的元素(到期的)追加到expired的末尾std::copy(timers_.begin(), end, back_inserter(expired));	timers_.erase(timers_.begin(), end);	//删除刚刚复制的定时器for (const Entry& it : expired){// 从activeTimers_中移除到期的定时器ActiveTimer timer(it.second, it.second->sequence());size_t n = activeTimers_.erase(timer);assert(n == 1); (void)n;}assert(timers_.size() == activeTimers_.size());return expired;
}

reset函数

void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
{Timestamp nextExpire;for (const Entry& it : expired){ActiveTimer timer(it.second, it.second->sequence());//timer sequenceif (it.second->repeat()	//有设置超时时间&& cancelingTimers_.find(timer) == cancelingTimers_.end())	//超时的timer,不在待删除队列中{it.second->restart(now);	//重置超时时间insert(it.second);		//重新加入timers_ 和 activeTimers_监听超时事件}else	//没有设置超时时间,或者刚刚超时的timer在执行超时回调时被加入了删除队列{// FIXME move to a free listdelete it.second; // FIXME: no delete please 删除timer指针}}if (!timers_.empty())	{nextExpire = timers_.begin()->second->expiration();	//最小的期望时间数}if (nextExpire.valid())	//最小的时间项数有效的话{resetTimerfd(timerfd_, nextExpire);	//重置定时器}
}

resetTimerfd()函数

// 重置定时器的超时时间,用到了timerfd_settime() expiration:用户定时器的超时时间

void resetTimerfd(int timerfd, Timestamp expiration)
{// wake up loop by timerfd_settime()struct itimerspec newValue;struct itimerspec oldValue;memZero(&newValue, sizeof newValue);memZero(&oldValue, sizeof oldValue);newValue.it_value = howMuchTimeFromNow(expiration);	//设置超时时间,未设置it_interva,定时器定时一次int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);//重新设置timerfd的超时时刻if (ret){LOG_SYSERR << "timerfd_settime()";}
}

用户传入的timer被保存在timers_中,设置一个timerfd和对应的channel,它超时后调用handleread(),在这里面获取所有超时的定时器,并且依次执行这些定时器的回调函数(用户传入),那么这个**timerfdChannel__**就应该时常被触发

找到timerfd固定的超时时间

在addTimerInLoop()中,如果新加入的定时器的超时时刻,在定时器队列中是最小的那个,那么把新加入的超时时刻与之前timerfd的超时时刻取差值 作为timefd_的新的超时时刻


http://chatgpt.dhexx.cn/article/wQG1C8te.shtml

相关文章

《muduo网络库》学习笔记——muduo学习总结

muduo是基于非阻塞的IO和事件驱动的网络库&#xff08;Reactor模式&#xff09;&#xff0c;其核心是一个事件循环EventLoop&#xff0c;用于响应计时器和IO事件。muduo采用基于对象&#xff08;object-based&#xff09;而非面向对象&#xff08;object-oriented&#xff09;的…

Ubuntu安装muduo库

1. 首先安装boost库&#xff1b; sudo apt-get update sudo apt-get install libboost-all-dev 2. 下载muduo库&#xff0c; https://github.com/chenshuo/muduo 3. 解压后进入解压目录&#xff0c;vim CMakeLists.txt&#xff0c;注释掉略过unit_test测试用例代码的编译&#…

linux muduo 编译安装,muduo记录

1.muduo编译安装 编译muduo遇见的报错可以在github上的issue上面查找。一般都能顺利解决,我遇到的就是没有安装boost-dev. centos7系统 执行: sudo yum install boost-dev 2.截取流程图 图片截取自《Linux多线程服务端编程&#xff1a;使用muduo C网络库》 3.源码摘录 摘录一个…

muduo源码分析之TcpServer模块

这次我们开始muduo源代码的实际编写&#xff0c;首先我们知道muduo是LT模式&#xff0c;Reactor模式&#xff0c;下图为Reactor模式的流程图[来源1] 然后我们来看下muduo的整体架构[来源1] 首先muduo有一个主反应堆mainReactor以及几个子反应堆subReactor&#xff0c;其中子反应…

muduo网络库学习(1)

muduo网络库学习&#xff08;1&#xff09; 文章目录 muduo网络库学习&#xff08;1&#xff09;前言一、muduo是什么&#xff1f;二、代码结构1.base库2.net库3.附属库 二、网络库结构总结 前言 本章节主要介绍muduo网络库的整体架构&#xff01;一、muduo是什么&#xff1f;…

muduo

muduo 概述 muduo是基于Reactor模式的网络库&#xff0c;用于响应计时器和IO事件。 muduo采用基于对象而非面向对象的设计风格&#xff0c;其事件回调采用functionbind&#xff0c;用户在使用muduo的时候不需要继承其中的class 架构 Multiple Reactor Reactor模式&#xff1a…

muduo日志库原理以及源码分析

muduo日志库特点 日志批量写入批量唤醒写线程写日志用notifywait_timeout 方式触发日志的写入锁的粒度&#xff0c;双缓冲&#xff0c;双队列buffer默认 4M 缓冲区&#xff0c; buffers 是 buffer 队列&#xff0c; push 、 pop 时使用 move 语义 减少内存拷贝 muduo的这些特点…

muduo网络库与服务模型介绍

目录 一、muduo网络库简介 1、特点 2、代码结构 &#xff08;1&#xff09;公共接口 &#xff08;2&#xff09;内部实现 二、muduo线程模型 1、单线程Reactor 2、Reactor线程池 3、one loop per thread 4、one loop per thread 线程池 muduo是陈硕个人使用C开发的一…

muduo 架构解析

muduo是一个基于Reactor模式的C网络库。它采用非阻塞I/O模型&#xff0c;基于事件驱动和回调。我们不仅可以通过muduo来学习linux服务端多线程编程&#xff0c;还可以通过它来学习C11。     Reactor是网络编程的一般范式。我们这里从reactor模式为出发点&#xff0c;根据R…

muduo库介绍

muduo库是一个多线程服务器开发库 muduo 作者陈硕&#xff0c;现在在美国加州硅谷某互联网大公司工作&#xff0c;从事大规模分布式的可靠系统工程。这个库是作者多年工作的总结&#xff0c;可以说大家学通了这个库&#xff0c;找一份Linux服务器开发的工作是没问题的&#xf…

C++ muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装

Muduo is a multithreaded C network library based on the reactor pattern. muduo库的介绍就是&#xff1a;一个基于reactor反应堆模型的多线程C网络库。 muduo网络库是C语言开发的一个非常优秀的网络库&#xff0c;作者陈硕&#xff0c;muduo网络库在多线程环境下性能非常高…

遗传算法示例

遗传的概念&#xff1a; 遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型&#xff0c;是一种通过模拟自然进化过程搜索最优解的方法。 遗传算法的特点&#xff1a; 对于搜索算法的共同特征有 首先组成一组候选解。依据某些使用性条件测算这些…

10分钟搞懂遗传算法

大自然有种神奇的力量&#xff0c;它能够将优良的基因保留下来&#xff0c;从而进化出更加强大、更加适合生存的基因。遗传算法便基于达尔文的进化论&#xff0c;模拟了自然选择&#xff0c;物竞天择、适者生存&#xff0c;通过N代的遗传、变异、交叉、复制&#xff0c;进化出问…

遗传算法简单实例

遗传算法的手工模拟计算示例 为更好地理解遗传算法的运算过程&#xff0c;下面用手工计算来简单地模拟遗传算法的各 个主要执行步骤。 例&#xff1a;求下述二元函数的最大值&#xff1a; (1) 个体编码 遗传算法的运算对象是表示个体的符号串&#xff0…

遗传算法(基础知识)

遗传算法&#xff08;基础知识&#xff09; 遗传算法简称GA&#xff08;Genetic Algorithms&#xff09;模拟自然界生物遗传学&#xff08;孟德尔&#xff09;和生物进化论&#xff08;达尔文&#xff09;通过人工方式所构造的一类 并行随机搜索最优化方法&#xff0c;是对生物…

遗传算法概念、步骤、应用解析(案例直白--黄含驰)

遗传算法 ①  在几十亿年的演化过程中&#xff0c;自然界中的生物体已经 形成了一种优化自身结构的内在机制&#xff0c;它们能够不 断地从环境中学习&#xff0c;以适应不断变化的环境  对于大多数生物体&#xff0c;这个过程是通过自然选择和有性生殖来完成的。自然选择…

遗传算法超详细图解

遗传算法&#xff08;Genetic Algorithm&#xff09;顾名思义&#xff0c;是一种基于自然选择原理和自然遗传机制的启发式搜索算法。该算法通过模拟自然界中生物遗传进化的自然机制&#xff08;选择、交叉和变异操作&#xff09;&#xff0c;将好的遗传基因&#xff08;最优目标…

遗传算法及其应用

一、遗传算法的定义 遗传算法的基本思想是参考生物学中物种“物竞天择&#xff0c;适者生存”的思想。在计算机中&#xff0c;通过给定约束条件&#xff0c;使初始参数不断迭代&#xff0c;从而接近最优解。 遗传算法可描述为&#xff1a; Initialize population process-chr…

详解遗传算法(含MATLAB代码)

目录 一、遗传算法概述 二、遗传算法的特点和应用 三、遗传算法的基本流程及实现技术 3.1 遗传算法的基本流程 3.2 遗传算法的实现技术 1.编码 2.适应度函数 3.选择算子 4.交叉算子 5.变异算子 6.运行参数 四、遗传算法的基本原理 4.1 模式定理 4.2 积木块假设 …

遗传算法步骤

遗传算法是一种模拟生物自然进化的一种算法&#xff0c;通话对生物进化的模拟&#xff0c;实现对数值函数的模拟计算。它主要分为四个步骤&#xff1a;初始化、杂交、变异和选择。相关实现可参考https://github.com/ShaquallLee/evolutionary-programming/tree/master/aEP 1、…