掌握C++定时器:构建自己的定时器的分步指南

简介: 本文是一份详细的、分步指南,旨在帮助读者掌握C++定时器的构建过程。通过本文,读者将了解到什么是定时器,以及为什么需要自己构建定时器而不仅仅使用标准库中的函数。文章将从基础开始,介绍了利用C++的基本语法和操作符创建一个简单的定时器的步骤。随后,文章逐渐深入,介绍了如何优化定时器的性能,包括减少延迟和提高精度。

一、定时器应用场景

(1)心跳检测。

(2)游戏中的技能冷却。

(3)倒计时。

(4)其他需要延迟处理的功能。

二、利用红黑树实现定时器

红黑树是绝对有序的数据结构。

在c++中,set、map、multiset、multimap使用的是红黑树管理数据。可以利用这几个类实现定时器方案,以set为例,使用C++ 14特性。

2.1、实现接口

获取当前时间的接口GetTick(),通过C++ 11时间库chrono:

(1)steady_clock:系统启动到当前的时间,可以用来计算程序运行时间。

(2)system_clock:时间戳,可以修改。

(3)high_resolution_clock:高精度版本的steady_lock。

int64_tGetTick()
{
autosc=chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
autotemp=chrono::duration_cast<milliseconds>(sc.time_since_epoch());
returntemp.count();
}

相同触发时间的定时任务处理方案:

(1)越后面插入的节点,插入位置放在红黑树越右侧。

(2)使用C++的set容器;内部是红黑树管理数据。

(3)定时器节点设计,使用触发时间和ID唯一标识定时节点。

structTimeNodeBase{
int64_tid;     // 描述插入的先后顺序time_texpire; // 触发时间}

(4)比较仿函数,确定数据插入红黑树的位置。利用C++14的特性,find时只需要等价key比较,无需构建key对象比较。利用基类的多态特性,只需要一个比较仿函数即可。

booloperator< (constTimeNodeBase*lhd,constTimeNodeBase*rhd)
{
if(lhd->expire<rhd->expire)
returntrue;
elseif(lhd->expire>rhd->expire)
returnfalse;
returnlhd->id<rhd->id;
}

(5)尽量减少函数对象赋值、移动等操作,提高性能。

// TimerNode 继承 TimerNodeBasestructTimerNode:publicTimerNodeBase{
// C++ 11特性,使用函数对象。降低拷贝消耗,提高效率usingCallback=std::function<void(constTimerNode&node)>;
Callbackfunc;
// 构造函数,只构造一次TimerNode(int64_tid,time_texpire,Callbackfunc):func(func){
this->id=id;
this->expire=expire;
    }
};

2.2、驱动定时器方式

可以采用IO时间和定时器事件在同一个线程执行的方案,利用epoll的epoll_wait()第四个参数做定时延时。

示例:

intmain()
{
intepfd=epoll_wait(1);
epoll_eventevs[64]={0};
while(1)
    {
intn=epoll_wait(epfd,evs,64,delaytime);
inti=0;
for(i=0;i<n;i++)
        {
/*处理IO事件*/        }
// 处理定时任务事件    }
return0;
}

2.3、示例代码

(1)创建定时器驱动,epoll_create、epoll_wait。

(2)创建timer类,实现AddTimer()、CheckTimer()、DelTimer()等接口。

(3)选择数据结构,set容器,本质使用红黑树数据结构。

(4)定义节点的结构。

demo代码:

#include <sys/epoll.h>#include <functional>#include <chrono>#include <set>#include <memory>#include <iostream>usingnamespacestd;
structTimerNodeBase{
time_texpire;
int64_tid;
};
// TimerNode 继承 TimerNodeBasestructTimerNode:publicTimerNodeBase{
// C++ 11特性,使用函数对象。降低拷贝消耗,提高效率usingCallback=std::function<void(constTimerNode&node)>;
Callbackfunc;
// 构造函数,只构造一次TimerNode(int64_tid,time_texpire,Callbackfunc):func(func){
this->id=id;
this->expire=expire;
    }
};
// 基类引用,多态特性booloperator<(constTimerNodeBase&lhd, constTimerNodeBase&rhd)
{
if (lhd.expire<rhd.expire)
returntrue;
elseif (lhd.expire>rhd.expire)
returnfalse;
returnlhd.id<rhd.id;
}
classTimer{
public:
statictime_tGetTick()
    {
/* C++ 11时间库chrono *///表示一个具体时间autosc=chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
autotmp=chrono::duration_cast<chrono::milliseconds>(sc.time_since_epoch());
returntmp.count();
    }
TimerNodeBaseAddTimer(time_tmsec, TimerNode::Callbackfunc)
    {
time_texpire=GetTick() +msec;
//避免拷贝、移动构造autoele=timermap.emplace(GenID(), expire, func);
returnstatic_cast<TimerNodeBase>(*ele.first);
    }
boolDelTimer(TimerNodeBase&node)
    {
// C++ 14新特性,不在需要传一个key对象,传递一个key的等价值autoiter=timermap.find(node);
if (iter!=timermap.end())
        {
timermap.erase(iter);
returntrue;
        }
returnfalse;
    }
boolCheckTimer()
    {
autoiter=timermap.begin();
if (iter!=timermap.end() &&iter->expire<=GetTick())
        {
iter->func(*iter);
timermap.erase(iter);
returntrue;
        }
returnfalse;
    }
time_tTimeToSleep()
    {
autoiter=timermap.begin();
if (iter==timermap.end())
return-1;//没有定时任务,设置epoll一直阻塞。time_tdiss=iter->expire-GetTick();
returndiss>0?diss : 0;
    }
private:
staticint64_tGenID()
    {
returngid++;
    }
staticint64_tgid;
set<TimerNode, std::less<>>timermap;
};
int64_tTimer::gid=0;
#define EPOLL_EV_LENFTH 1024intmain()
{
intepfd=epoll_create(1);
unique_ptr<Timer>timer=make_unique<Timer>();
inti=0;
timer->AddTimer(1000, [&](constTimerNode&node) {
cout<<Timer::GetTick() <<"node id:"<<Home|NODE.ID<<" revoked times:"<<++i<<endl;
    });
timer->AddTimer(1000, [&](constTimerNode&node) {
cout<<Timer::GetTick() <<"node id:"<<Home|NODE.ID<<" revoked times:"<<++i<<endl;
    });
timer->AddTimer(3000, [&](constTimerNode&node) {
cout<<Timer::GetTick() <<"node id:"<<Home|NODE.ID<<" revoked times:"<<++i<<endl;
    });
autonode=timer->AddTimer(2100, [&](constTimerNode&node) {
cout<<Timer::GetTick() <<"node id:"<<Home|NODE.ID<<" revoked times:"<<++i<<endl;
    });
timer->DelTimer(node);
cout<<"now time:"<<Timer::GetTick() <<endl;
epoll_eventevs[EPOLL_EV_LENFTH] = { 0 };
while (1)
    {
intnready=epoll_wait(epfd, evs, EPOLL_EV_LENFTH, timer->TimeToSleep());
for (inti=0; i<nready; i++)
        {
/*处理IO事件*/        }
// timer检测和处理while (timer->CheckTimer());
    }
return0;
}

总结

设计一个定时器时,先确定时间精度;选择驱动定时器的方式;选择合适的数据接口(不要选择优先队列);设计定时器基本接口 { AddTimer()、DelTimer()、CheckTimer() } 和扩展接口 { Tick()等 } ;考虑相同触发时间的定时任务处理。

关注公众号《Lion 莱恩呀》随时随地学习技术。

目录
相关文章
|
27天前
|
存储 机器学习/深度学习 算法
使用 OpenCV4 和 C++ 构建计算机视觉项目:1~5
使用 OpenCV4 和 C++ 构建计算机视觉项目:1~5
36 0
|
1月前
|
Linux C++
C++服务器开发之定时器设计方案
定时器应⽤:1.⼼跳检测 2.技能冷却 3.武器冷却 4.倒计时 5.其它需要使⽤超时机制的功能
23 0
C++服务器开发之定时器设计方案
|
3月前
|
存储 Cloud Native 编译器
C++ xmake构建
C++ xmake构建
|
3月前
|
存储 缓存 安全
高并发内存池实战:用C++构建高性能服务器(下)
高并发内存池实战:用C++构建高性能服务器
高并发内存池实战:用C++构建高性能服务器(下)
|
3月前
|
存储 缓存 Linux
高并发内存池实战:用C++构建高性能服务器(上)
高并发内存池实战:用C++构建高性能服务器
|
4月前
|
监控 API C++
利用C++构建PC端监控软件:实时屏幕录制
在今天的数字化世界中,监控软件变得越来越重要。无论是家庭用户需要监控他们的孩子,还是企业需要监控员工的电脑活动,实时屏幕录制是一种有效的方法。本文将向您介绍如何使用C++构建PC端监控软件,实现实时屏幕录制功能。此外,我们还将讨论如何自动提交监控到的数据到一个网站上。
235 0
|
4月前
|
算法 Ubuntu Java
【C++数据结构】智能指针的构建
【C++数据结构】智能指针的构建
|
6月前
|
C语言 C++
【哈夫曼树】基本概念、构建过程及C++代码
【哈夫曼树】基本概念、构建过程及C++代码
126 0
|
8月前
|
存储 Linux C++
【GTest】C++在Linux上如何安装构建GoogleTest
【GTest】C++在Linux上如何安装构建GoogleTest
184 0
|
8月前
|
开发框架 Dart IDE
【CMake】CMake构建C++代码(一)
【CMake】CMake构建C++代码(一)

热门文章

最新文章

相关产品

  • 云迁移中心