C++服务器开发之定时器设计方案

简介: 定时器应⽤:1.⼼跳检测 2.技能冷却 3.武器冷却 4.倒计时 5.其它需要使⽤超时机制的功能

定时器应⽤

  • ⼼跳检测
  • 技能冷却
  • 武器冷却
  • 倒计时
  • 其它需要使⽤超时机制的功能

适合定时器的数据结构有红黑树,最小堆,跳表,时间轮,其中最小堆实现的定时器最常见

最小堆的堆顶永远是最小的,超时检测时只需要从堆顶开始检测就行

时间获取与定时函数

由于是跨平台,所以不使用操作系统所提供的,

linux下可以使用timerfd,timerfd被抽象成fd文件,配合epoll_wait的timeout参数可以使心跳线程和工作线程处于同一线程,避免伪心跳包出现

c++提供三种获取时间的类

  1. std::chrono::system_clock
  2. std::chrono::steady_clock
  3. std::chrono::high_resolution_clock

由于system_clock是获取系统时间,如果系统时间被更改,或者网络校时,都会使时间被更改,所以不适合使用,steady_clock是只会自增(例如开机时间),high_resolution_clock在不同的系统中可能有不同的实现(通常它只是 std::chrono::steady_clock或 std::chrono::system_clock的别名),所以我们选择std::chrono::steady_clock

而定时则选取std::condition_variable::wait_until函数

定时器接口

最小堆实现

对于每一个任务都要有以下几个基本字段描述

  • 超时后调用的回调函数
  • 超时的时长
  • 以及一个该任务是否一直执行的标志

对于每一个定时器有几个基本方法描述

  • 初始化一个定时器
  • 添加任务
  • 执行到期任务

c++对象封装后的代码

timer.h
#pragma once
#include <functional>
#include <chrono>
#include <queue>
#include <mutex>
#include <condition_variable>

using timedTesk = std::function<void()>;
using timeDuration = std::chrono::milliseconds;
using timePoint = std::chrono::time_point<std::chrono::steady_clock>;

class timerNode {
   
public:
    bool operator < (const timerNode& obj) const {
    return point > obj.getPoint(); }

    timePoint getPoint() const {
    return point; }

    timerNode(int milliseconds, timedTesk fun, bool keep);
    timerNode(const timerNode& obj);

    void operator()() const {
    tesk(); }
    bool is_keep() const {
    return keep; }

    //刷新超时时间点
    void flush();
private:
    timedTesk       tesk;//定时任务
    bool            keep;//是否保活
    timeDuration    time;//超时时长
    timePoint       point;//执行的时间点
};

class timer
{
   
public:
    //添加任务
    void addTimer(int time, timedTesk tesk, bool keep);

    //执行定时器
    void start();

    //销毁定时器
    void destroy();

private:

    //执行到期任务:expire_timer
    void expire_timer();

private:
    //存放定时任务的小根堆
    std::priority_queue<timerNode, std::vector<timerNode>, std::less<timerNode>> queue;

    //保证小根堆线程安全
    std::mutex que_mtx;

    //定时器销毁标志
    bool is_destroy;

    //定时std::condition_variable::wait_until
    std::mutex mtx;
    std::condition_variable cv;
};
timer.cpp
#include "timer.h"
#include <climits>
#include <thread>

timerNode::timerNode(int milliseconds, timedTesk fun, bool keep)
    :time(milliseconds), tesk(fun), keep(keep)
{
   
    flush();
}

timerNode::timerNode(const timerNode& obj) :time(obj.time), tesk(obj.tesk), keep(obj.keep)
{
   
    flush();
}

void timerNode::flush()
{
   
    point = std::chrono::steady_clock::now() + time;
}

void timer::addTimer(int time, timedTesk tesk, bool keep)
{
   
    std::lock_guard<std::mutex> lock(que_mtx);
    queue.emplace(time, tesk, keep);
    cv.notify_one();
}

void timer::start()
{
   
    is_destroy = false;
    std::thread th(&timer::expire_timer, this);
    th.detach();
}

void timer::destroy()
{
   
    is_destroy = true;
}

void timer::expire_timer()
{
   
    while (!is_destroy)
    {
   
        std::unique_lock<std::mutex> lock(mtx);
        //获取堆顶时间,没有则wait一个最大时间
        auto time = queue.empty() ? std::chrono::steady_clock::now() + std::chrono::hours(INT_MAX) : queue.top().getPoint();
        if (cv.wait_until(lock, time) == std::cv_status::no_timeout) {
   //有新的定时任务加入
            //新任务中可能存在比之前超时时间更小的任务,通过continue刷新超时时间
            continue;
        }
        else {
   
            //处理超时任务
            while (!queue.empty() && queue.top().getPoint() < std::chrono::steady_clock::now())
            {
   
                timerNode node = queue.top();
                {
   
                    std::lock_guard<std::mutex> lock_(que_mtx);
                    queue.pop();
                    /*
                    如果tesk设置了keep为true,但是直接修改超时的时间点的话tesk对象在小根堆中的位置不会改变,所以只能通过先pop在push的方式更新tesk节点在小根堆中的位置
                    */
                    if (node.is_keep())
                        queue.emplace(node);
                }
                node();//执行tesk运算符重载函数
            }
        }
    }
}
目录
相关文章
|
2月前
|
定位技术
GPS北斗卫星同步时钟(时间同步服务器)建设施工部署方案
GPS北斗卫星同步时钟(时间同步服务器)建设施工部署方案
GPS北斗卫星同步时钟(时间同步服务器)建设施工部署方案
|
2月前
|
监控 容灾 定位技术
云服务器的容灾方案
云服务器的容灾方案
|
2月前
|
设计模式 编解码 算法
【C/C++ 虚函数以及替代方案】C++ 虚函数的使用开销以及替代方案(三)
【C/C++ 虚函数以及替代方案】C++ 虚函数的使用开销以及替代方案
88 0
|
2月前
|
存储 设计模式 编译器
【C/C++ 虚函数以及替代方案】C++ 虚函数的使用开销以及替代方案(一)
【C/C++ 虚函数以及替代方案】C++ 虚函数的使用开销以及替代方案
188 0
|
2月前
|
编解码 Linux C语言
探索C++与Live555实现RTSP服务器的艺术(一)
探索C++与Live555实现RTSP服务器的艺术
268 1
|
1月前
|
弹性计算 运维 Java
解决方案测评(高效构建企业门户网站方案)基于ecs&云效&云解析DNS&VPC结合的自搭建方案报告
该文档是一个关于使用ECS、云效、云解析DNS和VPC结合的自搭建方案报告。主要内容包括前言部分,可能详细探讨了如何集成这些阿里云服务以构建自定义系统。由于提供的内容有限,具体的实施方案和细节未在摘要中体现。
185 2
|
1月前
|
存储
服务器数据恢复—存储中卷被删除后重建新卷的数据恢复方案
服务器存储数据恢复环境: 某品牌FlexStorage P5730服务器存储,存储中有一组由24块硬盘组建的RAID5阵列,包括1块热备硬盘。 服务器存储故障: 存储中的2个卷被删除,删除之后重建了一个新卷。需要恢复之前删除的一个卷的数据。
|
1月前
|
网络协议
UDP服务器的并发方案
UDP服务器的并发方案
19 0
|
2月前
|
云安全 监控 安全
云服务器遇到DDOS的防护方案
DDoS攻击是云服务器面临的重要安全威胁之一。为了保障服务器的稳定性和安全性,我们需要采取多种措施进行防范
|
2月前
|
存储 关系型数据库 MySQL
服务器数据恢复—EVA存储异常断电重启后虚拟机无法启动的数据恢复方案
服务器存储数据恢复环境: 某品牌EVA8400,服务器上安装VMware ESXi虚拟化平台,虚拟机的虚拟磁盘包括数据盘(精简模式)+快照数据盘,部分虚拟机中运行oracle数据库和mysql数据库。 服务器存储故障&检测: 存储异常断电重启后,存储中一台虚拟机无法启动。工作人员推测故障原因是异常断电导致电源模块出现故障,清空cache后重新启动存储发现该虚拟机仍无法正常启动。

热门文章

最新文章