何删除定时器?MyLibco协程网络库定时器的设计
时间戳类(基本摘自muduo)
//Timestamp.h
namespace Tattoo{class Timestamp{public: Timestamp(); explicit Timestamp(int64_t microSecondsSinceEpoch); void swap(Timestamp &that) {std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_); } std::string toString() const; std::string toFormattedString() const; //微妙大于0就是 valid 的 bool valid() const {return microSecondsSinceEpoch_ > 0; } int64_t microSecondsSinceEpoch() const {return microSecondsSinceEpoch_; } //微秒转化为秒 time_t secondsSinceEpoch() const {return static_cast(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); } //得到现在的时间 static Timestamp now(); //获取一个无效时间,即时间等于0 static Timestamp invalid(); //一百万,一微秒等于百万分之一秒 static const int kMicroSecondsPerSecond = 1000 * 1000; private: int64_t microSecondsSinceEpoch_;};// 这里重载 < 号,在下文的multimap 中就会用到inline bool operator<(Timestamp lhs, Timestamp rhs){return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();}inline bool operator==(Timestamp lhs, Timestamp rhs){return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();}将返回两个事件时间差的秒数,注意单位!inline double timeDifference(Timestamp high, Timestamp low){int64_t diff = high.microSecondsSinceEpoch() - low.microSecondsSinceEpoch(); return static_cast(diff) / Timestamp::kMicroSecondsPerSecond;}//把秒转化为微秒,构造一个对象,再把它们的时间加起来,构造一个无名临时对象返回inline Timestamp addTime(Timestamp timestamp, double seconds){int64_t delta = static_cast(seconds * Timestamp::kMicroSecondsPerSecond); return Timestamp(timestamp.microSecondsSinceEpoch() + delta);}} // namespace Tattoo
(资料图片)
//Timestamp.cpp
using namespace Tattoo;Timestamp::Timestamp() : microSecondsSinceEpoch_(0){}Timestamp::Timestamp(int64_t microseconds) : microSecondsSinceEpoch_(microseconds){}std::string Timestamp::toString() const{char buf[32] = {0}; int64_t seconds = microSecondsSinceEpoch_ / kMicroSecondsPerSecond; int64_t microseconds = microSecondsSinceEpoch_ % kMicroSecondsPerSecond; //PRId64跨平台打印64位整数,因为int64_t用来表示64位整数,在32位系统中是long long int,64位系统中是long int //所以打印64位是%ld或%lld,可移植性较差,不如统一同PRID64来打印。 snprintf(buf, sizeof(buf) - 1, "%" PRId64 ".%06" PRId64 "", seconds, microseconds); return buf;}//把它转换成一个格式化字符串std::string Timestamp::toFormattedString() const{char buf[32] = {0}; time_t seconds = static_cast(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); int microseconds = static_cast(microSecondsSinceEpoch_ % kMicroSecondsPerSecond); struct tm tm_time; gmtime_r(&seconds, &tm_time); snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, microseconds); return buf;}Timestamp Timestamp::now(){struct timeval tv; gettimeofday(&tv, NULL); //获得当前时间,第二个参数是一个时区,当前不需要返回时区,就填空指针 int64_t seconds = tv.tv_sec; //取出秒数 return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);}Timestamp Timestamp::invalid(){return Timestamp();}
定时器
在这里,我是直接让协程在一段时间之后唤醒即可(runAfter),至于需不需要 repeat ,这个我也在思考当中,以后了解到了再加吧!!学习也就是一点一点积累的过程啦!!! //Timer.h
/*定时器类*/class Timer{public: Timer(Timestamp when); Timestamp expiration() const {return expire_; } void run() const; Timestamp expire_; //任务的超时时间 Routine_t *timer_rou_;};
//Timer.cpp
Timer::Timer(Timestamp when) : timer_rou_(get_curr_routine()), //一个定时器对应一个协程 expire_(when){}void Timer::run() const{cout << "由定时器唤醒对应协程" << endl; timer_rou_->Resume();}
定时器容器
.h 文件
class TimeHeap{public: TimeHeap(EventLoop *loop); ~TimeHeap(); Timer *addTimer(Timestamp when); void delTimer(Timer *timer); private: typedef std::pairEntry; typedef std::multimapTimerMap; // 超时之后的可读回调 void handleRead(); std::vectorgetExpired(Timestamp now); /* 重置超时的定时器 */ void reset(const std::vector&expired, Timestamp now); bool insert(Timer *timer); EventLoop *loop_; const int timerfd_; Channel timerfdChannel_; TimerMap timers_;};
.cpp 文件
namespace Tattoo{namespace detail{//创建 timerfdint createTimerfd(){int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (timerfd < 0) {std::cout << "Failed in timerfd_create" << std::endl; } return timerfd;}/* 计算超时时间与当前时间的时间差,并将参数转换为 api 接受的类型 */struct timespec howMuchTimeFromNow(Timestamp when){/* 微秒数 = 超时时刻微秒数 - 当前时刻微秒数 */ int64_t microseconds = when.microSecondsSinceEpoch() - Timestamp::now().microSecondsSinceEpoch(); if (microseconds < 100) {microseconds = 100; } struct timespec ts; // 转换成 struct timespec 结构返回 // tv_sec 秒 // tv_nsec 纳秒 ts.tv_sec = static_cast( microseconds / Timestamp::kMicroSecondsPerSecond); ts.tv_nsec = static_cast( (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000); return ts;}/* 读timerfd,避免定时器事件一直触发 */void readTimerfd(int timerfd, Timestamp now){uint64_t howmany; ssize_t n = ::read(timerfd, &howmany, sizeof(howmany)); std::cout << "TimerQueue::handleRead() " << howmany << " at " << now.toString() << std::endl; if (n != sizeof howmany) {std::cout << "TimerQueue::handleRead() reads " << n << " bytes instead of 8" << std::endl; }}/* 重置 timerfd 的超时时间 */void resetTimerfd(int timerfd, Timestamp expiration){struct itimerspec newValue; struct itimerspec oldValue; bzero(&newValue, sizeof newValue); bzero(&oldValue, sizeof oldValue); newValue.it_value = howMuchTimeFromNow(expiration); //到这个时间后,会产生一个定时事件 int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue); if (ret) {std::cout << "timerfd_settime()" << std::endl; }}} // namespace detail} // namespace Tattoousing namespace Tattoo;using namespace Tattoo::detail;TimeHeap::TimeHeap(EventLoop *loop) : loop_(loop), timerfd_(createTimerfd()), timerfdChannel_(loop, timerfd_), timers_(){// 设置自己独特的回调函数,并不是和普通的Channel 一样,直接唤醒了对应的协程 timerfdChannel_.setHandleCallback( std::bind(&TimeHeap::handleRead, this)); timerfdChannel_.enableReading();}TimeHeap::~TimeHeap(){timerfdChannel_.disableAll(); ::close(timerfd_); for (auto it = timers_.begin(); it != timers_.end(); ++it) {delete it->second; }}/* 添加一个定时器 ,返回定时器指针,会在 channel->addEpoll 函数中使用到,因为要删除对应的定时器*/Timer *TimeHeap::addTimer(Timestamp when){Timer *timer = new Timer(when); 如果当前插入的定时器 比队列中的定时器都早 则返回真 bool earliestChanged = insert(timer); //最早的超时时间改变了,就需要重置timerfd_的超时时间 if (earliestChanged) {//timerfd_ 重新设置超时时间,使得 timerfd 的定时事件始终是最小的 resetTimerfd(timerfd_, timer->expiration()); } return timer;}/* 删除一个定时器 */void TimeHeap::delTimer(Timer *timer){auto it = timers_.find(timer->expire_); if (it != timers_.end()) {timers_.erase(it); } return;}//timerfd 可读 的回调void TimeHeap::handleRead(){Timestamp now(Timestamp::now()); //先读取 readTimerfd(timerfd_, now); std::vectorexpired = getExpired(now); for (std::vector::iterator it = expired.begin(); it != expired.end(); ++it) {it->second->run(); //run->Resume() } reset(expired, now); //这里主要是改变 timerfd 的定时最小值}//获取所有超时的定时器std::vectorTimeHeap::getExpired(Timestamp now){std::vectorexpired; auto it = timers_.lower_bound(now); assert(it == timers_.end() || now < it->first); std::copy(timers_.begin(), it, back_inserter(expired)); timers_.erase(timers_.begin(), it); return expired;}void TimeHeap::reset(const std::vector&expired, Timestamp now){Timestamp nextExpire; for (std::vector::const_iterator it = expired.begin(); it != expired.end(); ++it) {delete it->second; } if (!timers_.empty()) //timers_ 不为空 {/*获取当前定时器集合中的最早定时器的时间戳,作为下次超时时间*/ nextExpire = timers_.begin()->second->expiration(); } //如果取得的时间 >0就改变 timerfd 的定时 if (nextExpire.valid()) {resetTimerfd(timerfd_, nextExpire); }}bool TimeHeap::insert(Timer *timer){bool earliestChanged = false; Timestamp when = timer->expiration(); auto it = timers_.begin(); if (it == timers_.end() || when < it->first) {earliestChanged = true; } timers_.insert(std::make_pair(when, timer)); return earliestChanged;}
OK,上面的就是具体的实现代码了,下面来说一下几个点:
1.如何添加定时器?
在我写的协程库中是这样实现的: Channel::addEpoll()->loop_->runAfter(10)->timerHeap_->addTimer()
2.如何删除定时器?
loop_->cancel()->timerHeap_->delTimer()
3.如何将timerfd与Eventloop 统一起来?
首先来看一下eventloop:
.h
#include "Callbacks.h"#include "Timestamp.h"#include#include#include "routine.h"namespace Tattoo{class Channel;class Epoll;class TimeHeap;class Timer;class RoutineEnv_t;class EventLoop{public: EventLoop(); ~EventLoop(); void loop(); // timers Timer *runAt(const Timestamp &time); Timer *runAfter(double delay); void cancel(Timer *timer); void updateChannel(Channel *channel); void removeChannel(Channel *channel); private: typedef std::vectorChannelList; Epoll *epoll_; TimeHeap *timerHeap_; ChannelList activeChannels_; RoutineEnv_t *rouEnv_;};} // namespace Tattoo
.cpp
#include#include "Channel.h"#include "Epoll.h"#include "MiniHeap.h"#include "EventLoop.h"using namespace Tattoo;const int kPollTimeMs = 10000; // 10 sEventLoop::EventLoop() : rouEnv_(get_curr_thread_env()), // 一个 eventloop 对应一个 Routine_env epoll_(new Epoll(this)), timerHeap_(new TimeHeap(this)) //在TimeHead初始化时,就会将 timerfd 加入 epoll 监听中{// std::cout << "EventLoop created " << this << std::endl; rouEnv_->envEventLoop_ = this; //关键点}EventLoop::~EventLoop(){}void EventLoop::loop(){while (1) {activeChannels_.clear(); int ret = epoll_->poll(kPollTimeMs, &activeChannels_); for (auto it = activeChannels_.begin(); it != activeChannels_.end(); ++it) {(*it)->handleEvent(); //事件分发,记得注册时间回调(一般就是 Resume()) } } std::cout << "EventLoop " << this << " stop looping" << std::endl;}Timer *EventLoop::runAt(const Timestamp &time){return timerHeap_->addTimer(time);}Timer *EventLoop::runAfter(double delay){Timestamp time(addTime(Timestamp::now(), delay)); runAt(time);}void EventLoop::cancel(Timer *timer){timerHeap_->delTimer(timer);}void EventLoop::updateChannel(Channel *channel){epoll_->updateChannel(channel);}void EventLoop::removeChannel(Channel *channel){epoll_->removeChannel(channel);}
4.定时器的组织方式(和 muduo 差不多,他用的是set,我用的是 multimap)
muduo定时器容器封装了 Timer.h里面保存的是超时时间和回调函数, TimerQueue.h使用set容器保存多个定时器, 然后在TimerQueue中使用timerfd_create创建一个timerfd句柄, 插入定时器A后先比较A的触发时间和TimerQueue的触发时间, 如果A的触发时间比其小就使用timerfd_settime重置TimerQueue的timerfd的触发时间, TimerQueue中的timerfd的触发时间永远与保存的定时器中触发时间最小的那个相同, 然后timerfd触发可读后, 遍历保存的多个定时器, 看看有没有同时到期的, 有执行回调函数
4.协程库中定时器的使用(与 libco 基本一样)
先行阅读:https://blog.csdn.net/liushengxi_root/article/details/88421955 主要函数(addEpoll):
void Channel::addEpoll(){//这里就设置的回调函数和 timerfd 设置的回调函数不一样哦 setHandleCallback(std::bind(&Channel::handleFun, this)); events_ |= kReadEvent; events_ |= kWriteEvent; update(); Timer *tmp = loop_->runAfter(10); //退出当前协程 get_curr_routine()->Yield(); //删除加入的 epoll 信息和对应定时器 loop_->removeChannel(this); loop_->cancel(tmp);}
事件到来会唤醒对应的协程,时间超时时 也会唤醒对应的协程(不会让其一直阻塞下去)
标签:
相关推荐:
最新新闻:
- 软件开发中的“OO”到底是什么?真实案例解析OO理论与实践-天天热门
- 推荐21个高质量图片网站 免费免版权值得收藏
- 世界最新:配置完Kafka集群后 通过JavaAPI方式来操作
- 什么是水冷机箱?水冷机箱和风冷机箱有什么区别?
- 如何解决显卡驱动无法正常安装?联想启天M6900介绍及驱动
- Office2003序列号有哪些?Office2003专业版序列号和注册码分享
- 如何屏蔽热点资讯广告提示框?热点资讯怎么彻底卸载?
- 360加速球怎样开启?加速球一直红色应该怎样解决?
- usb音箱没有声音?音响插电视上没声音怎么设置?
- 如何通过邮件群发工资条?outlook群发工资条失败咋办?
- pp助手怎么修复闪退?pp助手有什么用?
- sbsettings怎么设置?sbsettings设置的具体步骤
- Fc2视频打不开了怎么办?打开Fc2视频的步骤
- taobaoprotect.exe是什么进程?taobaoprotect.exe进程占用内存的解决方法
- java编程题:如何判断四个棋子连在一起?
- 出现此选项卡已经恢复是怎么回事?出现此选项卡已经恢复解决办法
- 全球时讯:免费下载国外视频的网站 你值得拥有
- 硬盘分区表都有什么修复方法?移动硬盘分区丢失后怎样找回?
- qq提取安装文件失败怎么办?压缩包无文件可提取咋回事?
- win10系统normal.dot在哪里?分享找到normal.dot文件的方法
- win7如何给文件设置密码?win7文件夹设置密码的具体步骤
- 如何做外链?做外链需要注意什么?
- 聚焦:为什么新拟态UI和可访问性是无法共存的?原因分析
- 东东助手显示无法连接服务器怎么办?模拟器常见问题及解决办法-天天热头条
- 【体验】Xoom评测:比iPad重50克 屏幕倒更大?
- secondary logon服务是什么?开启secondary logon服务详细方法
- 何删除定时器?MyLibco协程网络库定时器的设计
- 每日快播:泥巴潭:《新龙族》免费卷土重来 拒绝“菜鸟玩家”
- 3dmark05怎么安装?3dmark05注册码分享
- 数学思想方法猜想与反驳 反例反驳在学习中的用处|热点
- Teracopy怎么样?Teracopy功能的及使用方法
- “东南亚小腾讯”大撤退,全面撤出欧洲市场
- 观速讯丨QQ邮箱发邮件受限制怎么办?SMTP发送邮件限制的解决方案
- 压铸模的使用特点是什么?压铸模的使用特点及使用原则
- 穿越时光隧道与古钓鱼城“面对面”|当前观察
- 天天热议:直降1000元 华硕 ProArt 创 16 2022 年终大促
- 游戏神U要来了 AMD居然定在情人节上市 全球观天下
- 每日简讯:最高6GHz睿频 英特尔13代酷睿i9-13900KS带来超凡体验
- 《霍格沃茨之遗》PC配置需求公布:推荐显卡1080 Ti
- FILA最好的时候已经过去?
- 当前资讯!《死亡空间:重制版》将采用2D地图 更易于使用!
- 黑鲨科技陨落,故事令人唏嘘_世界热文
- 《龙背上的农家》现已发售 Steam褒贬不一
- 【世界热闻】Q4利润增速一正一负,宁王让亿纬难望项背?| 见智研究
- 怪兽交配3D动作RPG新作《怪兽宇宙》1月上线
- 初代诞生26年 宝可梦种类现已超过1000种
- 三个月期美元Libor突破2008年金融危机期间高点 天天资讯
- 用上游戏本同款技术!联想小新Pro 16将支持独显直连
- 【环球速看料】ZOL科技早餐:英特尔6GHz睿频处理器上架,苹果新春电影《过五关》发布
- 节奏音游《节奏萌芽》2月1日全平台发售|世界快看
- 23长江C1票面利率为4.7000%:快看
- 环球视讯!《P3P》《P4G》新宣传片 售价均为148港币
- 《英雄联盟》Faker:LCK赛区整体比LPL赛区更强_速递
- 城市营造游戏《寓言之地》公布 登陆PC、支持中文
- 【焦点热闻】Xbox德国:《极限竞速》新作、《红霞岛》发售日将在直面会公开
- 武汉人的神秘邻居,有点萌:全球视讯
- 苹果新春电影《过五关》发布,用iPhone 14 Pro拍京剧,库克发文_环球速讯
- 2022年Q4传统PC出货量下降28% PC市场热潮已结束
- 《GTA:三部曲-终极版》即将登陆Steam平台:环球即时看
- 国产新冠药研发,都到哪一步了?
- 全球快报:海南文旅大盘再现违建,被罚1.23亿
- 全球速读:十倍变焦超远摄 适马60-600mm F4.5-6.3 DG DN OS发布
- 当现实中的尸体,变成你在游戏中打的丧尸-世界热门
- 天天快消息!《天龙八部之乔峰传》25日韩国上映 甄子丹将录韩版跑男
- 动作片《灭世男孩》首曝剧照 山姆·雷米监制-全球观焦点
- 国产模拟经营游戏《学园构想家》限时试玩版1月16日上线
- 世界看点:越是善解人意,越不爱社交不想聊天?
- 女子上班第一天因已婚被辞 公司赔偿100元误工费
- 快消息!《生化危机4:重制版》中配PV公开 艾达王亮相
- 纵向卡牌构筑肉鸽游戏《铁轨与墓穴》 将于2023Q1发售
- 《隐秘的角落》公布新预告 1月18日登录Steam 每日观点
- B站游戏年度榜单:国产FPS+建造《重装前哨》新预告
- 《卧龙:苍天陨落》多人物中配CG首曝 吕布霸气登场
- B站公布2022年必玩游戏榜单:法环、战神5等上榜
- 小布助手四周年幕后:主动的人让AI更主动|快报