当前位置: 首页 > news >正文

基于时间轮的定时器

目录

  • 基于时间轮的定时器
    • 原理
    • 实现
    • 实现效果

基于时间轮的定时器

原理

此处输入图片的描述
图片是一个单层时间轮,当指针走到某一格上,就获取那一格上挂的任务将其执行。
当时如果时间跨度大的时候,格子数明显不够,那么就可以做成多级时间轮。
其实就是当低层的时间轮走了一圈,将它高一层的时间轮走一格,并且将挂在高层时间轮上的任务分配下来。

实现

文件#include"TimeWheel.h"

#include<functional>
#include<list>
#include<thread>
#include<mutex>

struct TimeStamp //时间戳
{
    int ms;
    int s;
    int min;
    bool operator==(TimeStamp timeStamp)
    {
        return timeStamp.ms==ms&&timeStamp.s==s&&timeStamp.min==min;
    }
};

struct Event     //挂在时间槽上的事件
{
    std::function<void(void)> call_back;
    TimeStamp tri_time;    //事件触发的时间
    int dur;               //多久触发
};

class TimeWheel  //时间轮
{
public:
    TimeWheel();
    ~TimeWheel();

    void Start();  //启动
    int AddTimer(int space,std::function<void(void)>& call_back); //添加定期器

private:
    void DoLoop();//主循环
    void InsertTimer(Event &event);  //插入事件到时间槽
    int getMS(const TimeStamp &timeStamp);
    void solvingEvents(std::list<Event>l); //解决时间槽上的事件
    void getNextTime(TimeStamp &nowTimeStamp,int dur);  //得到下个时间

private:
    std::list<Event> *callBackList = nullptr;  //当做时间槽上,每一个槽都是一个链表
    std::mutex _mutex;  //互斥量

    TimeStamp timeStamp;  //事件轮的时间

    int slot_ms;     //毫秒的时间槽数量
    int slot_s;      //秒的时间槽数量
    int slot_min;    //分的时间槽数量

    int step;        //最小的间隔时间
};

文件#include"TimeWheel.cpp"

#include"TimeWheel.h"
#include<iostream>

using namespace std;

TimeWheel::TimeWheel()
{
    cout<<"TimeWheel()"<<endl;
    timeStamp.ms = timeStamp.s = timeStamp.min = 0;

    slot_ms=10;
    slot_s=60;
    slot_min=60;

    step = 100;
    callBackList = new list<Event>[slot_ms+slot_s+slot_min];
}

TimeWheel::~TimeWheel()
{
    delete[] callBackList;
}

void TimeWheel::Start()   //开启一个线程
{
    cout<<"Start()"<<endl;
    std::thread myThread([&]{
        this->DoLoop();
    });

    myThread.detach();
}

void TimeWheel::DoLoop()  //主循环
{
    cout<<"start"<<endl;
    while(true)
    {
        this_thread::sleep_for(chrono::milliseconds(step)); //让线程休息step毫秒的时间

        unique_lock<std::mutex> lock(_mutex);

        cout<<"time:"<<timeStamp.min <<" "<<timeStamp.s <<" "<<timeStamp.ms<<endl;
        TimeStamp per = timeStamp;        //per是原来的时间
        getNextTime(timeStamp,step);      //timeStamp向后走一步


        if(per.min!=timeStamp.min)  //分针有进位
        {
            //cout<<"(check min :" << slot_ms+slot_s+timeStamp.min <<")"<<endl;
            list<Event>& l = callBackList[slot_ms+slot_s+timeStamp.min];
            solvingEvents(l);
            l.clear();
        }
        else if(per.s!=timeStamp.s) //秒针有进位
        {
            //cout<<"(check s :" << slot_ms+timeStamp.s <<")"<<endl;
            list<Event>& l = callBackList[slot_ms+timeStamp.s];
            solvingEvents(l);
            l.clear();
        }
        else if(per.ms!=timeStamp.ms) //毫秒有进位
        {
            //cout<<"(check ms :" << timeStamp.ms <<")"<<endl;
            list<Event>& l = callBackList[timeStamp.ms];
            solvingEvents(l);
            l.clear();
        }

        lock.unlock();
    }
}

int TimeWheel::getMS(const TimeStamp &timeStamp)  //得到时间戳timeStamp一共多少ms
{
    return step * timeStamp.ms + timeStamp.s * 1000 + timeStamp.min * 60 * 1000;
}

void TimeWheel::solvingEvents(list<Event>l)
{
    for (auto item = l.begin(); item != l.end(); item++)
    {
        if(timeStamp == item->tri_time)  //触发时间到了
        {
            item->call_back();           //执行函数

            //如果需要发生多次加入下面两行
            getNextTime(item->tri_time,item->dur);
            InsertTimer(*item);
        }
        else
        {
            InsertTimer(*item);
        }
    }
}

void TimeWheel::getNextTime(TimeStamp &nowTimeStamp,int dur)//获得下一个时间
{
    int next_ms = getMS(nowTimeStamp)+dur;

    nowTimeStamp.min = next_ms/1000/60%slot_min;
    nowTimeStamp.s = (next_ms%(1000*60))/1000;
    nowTimeStamp.ms = (next_ms%1000)/step;
}

//添加定时器
int TimeWheel::AddTimer(int space,function<void(void)>& call_back)
{
    if(space<step)return -1;  //发生事件小于最小间隔
    if(space%step!=0)return -1;

    unique_lock<std::mutex> lock(_mutex);

    Event event;
    event.call_back = call_back;
    event.tri_time.ms = timeStamp.ms;
    event.tri_time.s = timeStamp.s;
    event.tri_time.min = timeStamp.min;
    event.dur = space;
    getNextTime(event.tri_time,event.dur);

    //cout<<"add a "<<space<<" clock"<<endl;
    cout<<event.tri_time.min<<":"<<event.tri_time.s<<":"<<event.tri_time.ms<<endl;
    InsertTimer(event);

    lock.unlock();
    return 0;
}

//先时间轮内插入事件
void TimeWheel::InsertTimer(Event &event)
{
    if(event.tri_time.min != timeStamp.min)   //分钟与现在不同的分钟就插入分的槽
        callBackList[slot_ms + slot_s + event.tri_time.min].push_back(event);
    else if(event.tri_time.s != timeStamp.s)
        callBackList[slot_ms + event.tri_time.s].push_back(event);
    else if(event.tri_time.ms != timeStamp.ms)
        callBackList[event.tri_time.ms].push_back(event);
}

测试文件Main.cpp

#include<cstdio>
#include<cstring>
#include"TimeWheel.h"
#include<iostream>

using namespace std;

void fun100()
{
    cout << "------------fun 100--------------" << endl;
}

void fun200()
{
    cout << "------------fun 200--------------" << endl;
}

void fun500()
{
    cout << "------------fun 500--------------" << endl;
}

void fun1500()
{
    cout << "------------fun 1500-------------" << endl;
}

int main()
{
    function<void(void)> f100 = std::bind(&fun100);
    function<void(void)> f200 = std::bind(&fun200);
    function<void(void)> f500 = std::bind(&fun500);
    function<void(void)> f1500 = std::bind(&fun1500);

    TimeWheel timeWheel;
    timeWheel.Start();

    //加入定时器
    timeWheel.AddTimer(100,f100);
    timeWheel.AddTimer(200,f200);
    timeWheel.AddTimer(500,f500);
    timeWheel.AddTimer(1500,f1500);
    while(true){}
    return 0;
}

实现效果

此处输入图片的描述

转载于:https://www.cnblogs.com/xcantaloupe/p/10703405.html

相关文章:

  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • 前后端分离,tomcat特殊字符不识别问题
  • 纯前端的分页(利用vant的List组件)
  • 汉诺塔问题(Hanoi Tower)递归算法解析(Python实现)
  • 数据库(MySQL)
  • Hive分桶
  • 第三章:内存分配与回收策略
  • visual studio 命令行 build
  • 摘要商城总结
  • StringBuufer与StringBulder线程的区别
  • 微信分享到朋友圈,怎么自定义分享的标题,图片,内容?
  • BZOJ2300[HAOI2011]防线修建——非旋转treap+凸包(平衡树动态维护凸包)
  • 今日学习20190425
  • MAYA 卸载工具,完美彻底卸载清除干净maya各种残留注册表和文件
  • 跨域的理解,以及解决方案!
  • CSS盒模型深入
  • EOS是什么
  • Fabric架构演变之路
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • Java,console输出实时的转向GUI textbox
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • MySQL用户中的%到底包不包括localhost?
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Python3爬取英雄联盟英雄皮肤大图
  • React 快速上手 - 07 前端路由 react-router
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • 仿天猫超市收藏抛物线动画工具库
  • 后端_ThinkPHP5
  • 基于Android乐音识别(2)
  • 解析 Webpack中import、require、按需加载的执行过程
  • 模型微调
  • 前端_面试
  • 如何进阶一名有竞争力的程序员?
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 使用权重正则化较少模型过拟合
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #define 用法
  • #微信小程序(布局、渲染层基础知识)
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (1) caustics\
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (九)c52学习之旅-定时器
  • (六)vue-router+UI组件库
  • (三)模仿学习-Action数据的模仿
  • (转)scrum常见工具列表
  • **PHP分步表单提交思路(分页表单提交)
  • .NET CLR Hosting 简介
  • .net core webapi 大文件上传到wwwroot文件夹
  • .net core使用ef 6
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .NET使用存储过程实现对数据库的增删改查