C++工具:C++高精度定时器--提供自定义精度回调定时器

//!
//! C++工具:C++高精度定时器--提供自定义精度回调定时器
//!
//! == 高精度定时器简介 ==
//! 定时器是网络编程中常见的组件之一,通常用于处理超时任务,
//!     定时器在普通程序中虽不常用,但需要时却无从下手
//! 本次提供支持自定义精度的定时器,采用多线程回调触发定时任务,
//!     采用了C++ chrono库进行计时,精度达到纳秒级别
//! 定时器的事件循环与任务回调都在子线程中完成,不影响主线程执行流程,
//!     但需要注意任务回调时的多线程问题
//! 该定时器工具在使用上依旧遵从简单便捷的原则
//! == 高精度定时器简介 ==
//!
//!
//! == 使用说明 ==
//! 类声明:
//!     template<class time_level = milliseconds,int time_len = 25,int th_size = 4>
//!     class Ttimer
//!
//! 参数:
//!     time_level : 时间精度
//!     time_len   : 间隔长度
//!     th_size    : 线程数量
//!
//! 类说明:
//!     time_level 和 time_len 是控制事件循环的扫描精度,由C++ chrono库控制,
//!         th_size 参数为线程池创建数量,三个参数都有默认值,
//!         Ttimer类在构造函数即启动事件循环
//!     Ttimer类的任务队列使用链表,按任务触发时间升序排列模拟最小堆,在取任务时有 O1 性能,
//!         但插入时未优化达到 On 级别,如有需求可采用二分法快速插入
//!
//! 快捷使用:
//!     默认创建:
//!         Ttimer<> tr;  //milliseconds精度(毫秒),25ms间隔,4线程
//!
//!     创建定时任务:
//!         //定时200毫秒 ,无限次触发,重复加入任务队列
//!         tr.add_task(ms(200),[](){
//!             cout<<"hellow world"<<endl;
//!         },-1);
//!
//!         //定时5秒 ,3次触发后移出任务队列
//!         tr.add_task(ss(5),[](){
//!             cout<<"hellow world"<<endl;
//!         },3);
//! == 使用说明 ==
//!
//!
//! == 精度定义 ==
//! C++ chrono库命名:
//!     using nanoseconds	= duration<_GLIBCXX_CHRONO_INT64_T, nano>;
//!     using microseconds	= duration<_GLIBCXX_CHRONO_INT64_T, micro>;
//!     using milliseconds	= duration<_GLIBCXX_CHRONO_INT64_T, milli>;
//!     using seconds	= duration<_GLIBCXX_CHRONO_INT64_T>;
//!     using minutes	= duration<_GLIBCXX_CHRONO_INT64_T, ratio< 60>>;
//!     using hours		= duration<_GLIBCXX_CHRONO_INT64_T, ratio<3600>>;
//!
//! 快速命名:
//!     using nan = nanoseconds;
//!     using mic = microseconds;
//!     using mil = milliseconds;
//!     using sec = seconds;
//!     using minu = minutes;
//!     using hour = hours;
//!     using us = microseconds;
//!     using ms = milliseconds;
//!     using ss = seconds;
//! == 精度定义 ==
//!
//!
//! == 展示顺序 ==
//! 1.使用测试
//! 2.Ttimer.h 文件
//! 3.部分测试结果
//! == 展示顺序 ==
//!
//! 结束语:
//!     定时器在插入时算出时间并时间按顺序插入到容器中,事件循环定时扫描容器的首位数据,
//!         达到时间之后放入线程池中运行
//!     虽然扫描精度可达到纳米级别,但触发时间达不到该精度,出现大量定时任务时,
//!         任务执行定时会被线程池拖慢,需要保证定时任务能快速执行并退出
//!     值得注意的是,定时器提供扫描精度的自定义,扫描精度越低,占用的性能越小,
//!         但执行任务的误差时间就越大,扫描精度与定时时长通常在 50 ~ 100 倍之间
//!
//!
#include <iostream>
#include <unistd.h>

#include "../include/Ttimer.h"

using namespace std;

void test_1()
{
    cout<<"===== test ====="<<endl;
    cout<<"in time_level  : "<<Ttimer<>::time_now().count()<<endl;
    cout<<"in ctime       : "<<(int)time(NULL)<<endl;

    auto fn = [=](){
        static int i=0;
        cout<<endl;
        cout<<"count       : "<<++i<<endl;
        cout<<"thread      : "<<this_thread::get_id()<<endl;
        cout<<"nanoseconds : "<<steady_clock::now().time_since_epoch().count()<<endl;
        cout<<"time_level  : "<<Ttimer<>::time_now().count()<<endl;
        cout<<"ctime       : "<<(int)time(NULL)<<endl;
    };

    //== 单独测试 ==
    int in = 1;
    switch (in) {
    case 1:
    {
        //默认精度 (检查:25ms)(触发:100ms)
        {
            cout<<"== test 1 =="<<endl;
            Ttimer<> tr;
            tr.add_task(ms(100),fn,-1);
            sleep(5);
        }
    }
    break;
    case 2:
    {
        //高精度 (检查:10us)(触发:200us)
        {
            cout<<"== test 2 =="<<endl;
            Ttimer<us,10> tr;
            tr.add_task(us(200),fn,-1);
            sleep(5);
        }
    }
    break;
    case 3:
    {
        //低精度 (检查:200ms)(触发:1s)
        {
            cout<<"== test 3 =="<<endl;
            Ttimer<ms,200> tr;
            tr.add_task(ss(1),fn,-1);
            while(true){ sleep(10); }
        }
    }
    break;
    case 4:
    {
        //短延时任务 (检查:10s)(触发:1minu)
        {
            cout<<"== test 4 =="<<endl;
            Ttimer<ss,10> tr;
            tr.add_task(minu(1),fn,3);
            while(true){ sleep(10); }
        }
    }
    break;
    case 5:
    {
        //长延时任务 (检查:1minu)(触发:1hour)
        {
            cout<<"== test 4 =="<<endl;
            Ttimer<minu,1> tr;
            tr.add_task(hour(1),fn,3);
            while(true){ sleep(10); }
        }
    }
    break;
    }
}


void test_2()
{
    cout<<"===== test_2 ====="<<endl;
    cout<<"in time_level  : "<<Ttimer<>::time_now().count()<<endl;
    cout<<"in ctime       : "<<(int)time(NULL)<<endl;

    auto fn = [=](){
        static int i=0;
        cout<<endl;
        cout<<"count       : "<<++i<<endl;
        cout<<"thread      : "<<this_thread::get_id()<<endl;
        cout<<"nanoseconds : "<<steady_clock::now().time_since_epoch().count()<<endl;
        cout<<"time_level  : "<<Ttimer<>::time_now().count()<<endl;
        cout<<"ctime       : "<<(int)time(NULL)<<endl;
    };

    //== 单独测试 ==
    int in = 1;
    switch (in) {
    case 1:
    {
        //默认方式使用
        {
            cout<<"== test 1 =="<<endl;
            Ttimer<> tr;
            tr.add_task(ms(100),fn,-1);
            sleep(5);
        }
    }
    break;
    case 2:
    {
        //单线程方式使用 (启用两个线程,保证事件循环和回调线程可运行)
        {
            cout<<"== test 2 =="<<endl;
            typedef Ttimer<milliseconds,25,2> Ttimer_sig;

            Ttimer_sig tr;
            tr.add_task(ms(100),fn,-1);
            sleep(5);
        }
    }
    break;
    case 3:
    {
        //取消定时任务与停止定时器
        {
            cout<<"== test 3 =="<<endl;
            Ttimer<> tr;

            auto fn1 = [=](){
                static int i=0;
                cout<<endl;
                cout<<"count       : "<<++i<<endl;
                cout<<"== fn1 =="<<endl;
            };

            auto fn2 = [=](){
                static int i=0;
                cout<<endl;
                cout<<"count       : "<<++i<<endl;
                cout<<"== fn2 =="<<endl;
            };

            int id1 = tr.add_task(ms(500),fn1,-1);
            int id2 = tr.add_task(ss(1),fn2,-1);
            sleep(3);
            cout<<"size: "<<tr.get_task_size()<<endl;

            cout<<"移除任务: id1"<<endl;
            tr.remove_task(id1);
            sleep(2);
            cout<<"size: "<<tr.get_task_size()<<endl;

            cout<<"关闭定时器"<<endl;
            tr.stop_timer();
            sleep(1);
            cout<<"size: "<<tr.get_task_size()<<endl;
        }
    }
    break;
    case 4:
    {
        //大量定时器启动 (最好分配足够的线程处理任务,否则会导致定时任务超时)
        {
            cout<<"== test 4 =="<<endl;
            Ttimer<ms,25,16> tr;    //16线程
            mutex mut;
            int sum = 0;
            auto fn1 = [&](int num){
                lock_guard<mutex> lock(mut);
                sum++;
                static int i=0;
                cout<<endl;
                cout<<"thread      : "<<this_thread::get_id()<<endl;
                cout<<"count       : "<<++i<<endl;
                cout<<"num         : "<<num<<endl;
                cout<<"ctime       : "<<(int)time(NULL)<<endl;
                cout<<"sum: "<<sum<<endl;
            };

            for(int i=0;i<100;i++)
            {
                tr.add_task(ms(200),bind(fn1,i),-1);
                cout<<"=="<<endl;
            }
            sleep(5);
        }
    }
    break;
    }
}


int main()
{
    cout<<"== begin =="<<endl;

//    test_1();
//    test_2();

    cout<<"== end =="<<endl;
    return 0;
}
//!
//! Ttimer.h
//!
#ifndef TTIMER_H
#define TTIMER_H

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>

//== vpool_th ==
template<int th_size>
class vpool_th
{
public:
    //创建线程池
    vpool_th()
    {
        //准备一个循环函数--给线程池内的线程[等待任务/执行任务]
        auto create_func = [=](){
            while(true)
            {
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> lock(_mutex);              //独占锁--获取队列任务
                    while (_tasks.empty() && _run) { _cond.wait(lock); }    //假唤醒--退出且队列为空
                    if(_run == false && _tasks.empty()) { return; }         //等待队列任务完成并退出任务
                    task = std::move(_tasks.front()); _tasks.pop();         //取任务
                }
                task(); //执行从队列获取的任务函数
            }
        };
        for(size_t i = 0;i<th_size;i++) { _workers.emplace_back(create_func); }
    }

    //释放线程池
    ~vpool_th()
    {
        { std::unique_lock<std::mutex> lock(_mutex); _run = false; }    //这里锁的用处--add_work执行时不给释放
        _cond.notify_all();                                             //唤醒所有线程准备退出
        for(std::thread &worker: _workers) { worker.join(); }           //等待所有线程完成任务后释放
    }

    //加入任务函数
    //      typename std::result_of<Tfunc(Targs...)>::type -- 获取外部函数的返回值类型
    template<class Tfunc, class... Targs>
    auto add_work(Tfunc&& func, Targs&&... args)
        -> std::future<typename std::result_of<Tfunc(Targs...)>::type>
    {
        using ret_type = typename std::result_of<Tfunc(Targs...)>::type;                //任务函数的返回类型
        auto pack = std::bind(std::forward<Tfunc>(func), std::forward<Targs>(args)...); //任务函数打包
        auto task = std::make_shared<std::packaged_task<ret_type()>>(pack);             //打包为连接future类
        auto res = task->get_future();                                                  //从future类获取函数返回值
        {
            std::unique_lock<std::mutex> lock(_mutex);              //锁住并准备将任务插入队列
            std::function<void()> func = [task](){ (*task)(); };    //包装外部任务函数到function
            if(_run) { _tasks.emplace(func); }                      //插入function到队列
        }
        _cond.notify_one(); //通知一个线程去完成任务
        return res;
    }

private:
    bool _run = true;                           //运行标记
    std::vector<std::thread> _workers;          //线程容器
    std::mutex _mutex;                          //线程池锁
    std::queue<std::function<void()>> _tasks;   //任务队列
    std::condition_variable _cond;              //条件变量
};
//== vpool_th ==


#include <chrono>
#include <mutex>
#include <memory>
#include <list>

using namespace std::chrono;
using nan = nanoseconds;
using mic = microseconds;
using mil = milliseconds;
using sec = seconds;

using minu = minutes;
using hour = hours;

using us = microseconds;
using ms = milliseconds;
using ss = seconds;

//== Ttimer ==
template<class time_level = milliseconds,int time_len = 25,int th_size = 4>
class Ttimer
{
public:
    struct task
    {
        int id;                     //定时器任务ID
        int active;                 //活动数规定执行次数 [-1 : 无限次]
        time_level time_start;      //任务开始时间
        time_level time_delay;      //延时的时间长度
        std::function<void()> task; //回调函数
    };

public:
    Ttimer()
    {
        //处理任务
        auto fn_process_task = [=](time_level time){
            while(_ls_task.size() > 0) //获取多个超时任务
            {
                auto pack = _ls_task.begin();
                if(pack->time_start <= time)
                {
                    task ct = *pack;            //保存指针指向的数据
                    _pool.add_work(pack->task); //加入任务到线程执行
                    remove_task(pack);          //移除已执行任务

                    //从保留数据重新加入到定时任务
                    if(ct.active != 0)
                    {
                        if(ct.active > 0) ct.active -= 1;       //设置剩余活跃数
                        ct.time_start = time + ct.time_delay;   //重新计算延时时间
                        add_task_ls({ct.id,ct.active,ct.time_start,ct.time_delay,ct.task});
                    }
                }
                else break;
            }
        };

        //定时器事件循环
        auto fn_event_loop = [=](){
            while(_run)
            {
                auto now = time_now();  //更新时间
                fn_process_task(now);   //处理任务
                std::this_thread::sleep_for(time_level(time_len));
            }
            remove_task_all();
        };
        _pool.add_work(fn_event_loop); //启动事件循环
    }

    //加入任务
    template<class Ttime>
    inline int add_task(Ttime delay,std::function<void()> fn,int active = -1)
    {
        auto pos_start = time_now() + duration_cast<time_level>(delay);
        int id = _task_id++;
        if(active != -1) active -= 1;
        add_task_ls({id,active,pos_start,delay,fn});
        return id;
    }

    //移除定时任务
    void remove_task(int id)
    {
        std::lock_guard<std::mutex> lock(_mut_ls);
        for(auto it = _ls_task.begin();it != _ls_task.end();it++)
        {
            if(it->id == id)
            { _ls_task.erase(it); break; }
        }
    }

    //移除定时任务--迭代器重载
    void remove_task(typename std::list<task>::iterator it)
    {
        std::lock_guard<std::mutex> lock(_mut_ls);
        _ls_task.erase(it);
    }

    //移除定时任务--所有
    void remove_task_all()
    {
        std::lock_guard<std::mutex> lock(_mut_ls);
        _ls_task.clear();
    }

    //停止定时器
    void stop_timer() { _run = false; }

    //获取定时器排队任务数(仅代表当前任务长度,无限定时任务会重复加入)
    size_t get_task_size() { return _ls_task.size(); }

    //获取现在时间
    static inline time_level time_now()
    {
        nanoseconds now = steady_clock::now().time_since_epoch();
        return duration_cast<time_level>(now);
    }

private:
    bool _run = true;           //事件循环
    int _task_id = 1;           //任务ID
    std::mutex _mut_ls;         //定时器任务锁
    vpool_th<th_size> _pool;    //线程池
    std::list<task> _ls_task;   //定时器任务容器,保证从小到大排序

    //插入到定时任务,保证顺序:从小到大 (模拟最小堆数据结构)
    void add_task_ls(const task &pack)
    {
        std::lock_guard<std::mutex> lock(_mut_ls);
        for(auto it = _ls_task.begin();it != _ls_task.end();it++)
        {
            if(pack.time_start <= it->time_start)
            { _ls_task.insert(it,pack); return; }
        }
        _ls_task.push_back(pack);
    }
};
//== Ttimer ==

#endif // TTIMER_H
/*
 * test_1 : 1
 *
== begin ==
===== test =====
in time_level  : 325978552
in ctime       : 1689066461
== test 1 ==

count       : 1
thread      : 139718514865856
nanoseconds : 325978655828058
time_level  : 325978655
ctime       : 1689066461

count       : 2
thread      : 139718506473152
nanoseconds : 325978757588587
time_level  : 325978757
ctime       : 1689066461

count       : 3
thread      : 139718523258560
nanoseconds : 325978859803724
time_level  : 325978859
ctime       : 1689066461
。。。。。。

 *
 * test_1 : 2
 *
== begin ==
===== test =====
in time_level  : 326060331
in ctime       : 1689066543
== test 2 ==

count       : 1
thread      : 140377117058752
nanoseconds : 326060333458621
time_level  : 326060333
ctime       : 1689066543

count       : 2
thread      : 140377125451456
nanoseconds : 326060333770524
time_level  : 326060333
ctime       : 1689066543

count       : 3
thread      : 140377133844160
nanoseconds : 326060334068244
time_level  : 326060334
ctime       : 1689066543
。。。。。。

 *
 * test_1 : 3
 *
== begin ==
===== test =====
in time_level  : 326111485
in ctime       : 1689066594
== test 3 ==

count       : 1
thread      : 140351928170176
nanoseconds : 326112489628221
time_level  : 326112489
ctime       : 1689066595

count       : 2
thread      : 140351919777472
nanoseconds : 326113492024694
time_level  : 326113492
ctime       : 1689066596

count       : 3
thread      : 140351936562880
nanoseconds : 326114495201979
time_level  : 326114495
ctime       : 1689066597
。。。。。。

 *
 * test_1 : 4
 *
== begin ==
===== test =====
in time_level  : 326176446
in ctime       : 1689066659
== test 4 ==

count       : 1
thread      : 140593960482496
nanoseconds : 326236451654969
time_level  : 326236451
ctime       : 1689066719

count       : 2
thread      : 140593952089792
nanoseconds : 326296455636278
time_level  : 326296455
ctime       : 1689066779

count       : 3
thread      : 140593943697088
nanoseconds : 326356461103726
time_level  : 326356461
ctime       : 1689066839
。。。。。。

 *
 * test_1 : 5
 *
== begin ==
===== test =====
in time_level  : 318480718
in ctime       : 1689058719
== test 4 ==

count       : 1
thread      : 139916395837120
nanoseconds : 322080757612433
time_level  : 322080757
ctime       : 1689062563

count       : 2
thread      : 139916387444416
nanoseconds : 325680800607580
time_level  : 325680800
ctime       : 1689066163
。。。。。。
*/

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值