C++定时器实现定时任务

1.1、单次定时任务实现

boost 的asio库里有几个定时器,老的有 deadline_timer , 还有三个可配合 C++11 的 chrono 使用的 high_resolution_timer 、 steady_timer 和 system_timer 。deadline_timer是asio早期版本提供的定时器,使用boost.date_time库提供时间支持,deadline_timer会被逐渐淘汰。

仅仅固定时间后去执行某任务,可以使用boost中的asio库提供了steady_timer定时器,定时器的用法也是比较简单的,基本上分三步。创建 io_service , 创建timer 并设置等待时间, 调用wait 或async_wait 等待.

其中wait是同步等待,async_wait是异步等待,需要给一个回调给它。

具体实现如下:

#include <iostream>
#include <boost/asio/io_service.hpp>
#include <boost/asio/steady_timer.hpp>
#include <thread>

void executeMission()
{
    std::cout<<"excute mission"<<std::endl;
}

int main()
{
    boost::asio::io_context io_ctx;
    boost::asio::steady_timer asio_steady_timer{io_ctx,
                std::chrono::seconds{3}};
    asio_steady_timer.async_wait([](const boost::system::error_code &ec)
        {std::cout<<"excute mission"<<std::endl;});
    //asio_steady_timer.async_wait(&executeMission);
    std::cout<<"start io_service"<<std::endl;

    io_ctx.run();

    return 0;
}

1.2、执行固定次数的定时任务

为了实现重复定时,需要在回调函数里修改steady_timer的过期时间,因此要把steady_timer传给回调函数:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/bind.hpp>
int count = 0;

void executeMission(const boost::system::error_code& e, boost::asio::steady_timer *t)
{
    std::cout << "execute mission" << std::endl;
    if(count < 10) {
        ++count;
        t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));
        t->async_wait(boost::bind(executeMission, boost::asio::placeholders::error, t));
    }
}

int main()
{
    boost::asio::io_context io_ctx;
    boost::asio::steady_timer t(io_ctx, boost::asio::chrono::seconds(1));
    t.async_wait(boost::bind(executeMission, boost::asio::placeholders::error, &t));
    io_ctx.run();
    std::cout << "time: " << count << std::endl;
    return 0;
}

用类封装下:

#include <iostream>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_timer.hpp>


class printer {
    private:
        boost::asio::io_context io_;
        boost::asio::steady_timer timer_;
        int count_;
        void print() {
            if (count_ < 500) {
                std::cout << count_ << "\n";
                ++count_;

                timer_.expires_from_now(std::chrono::milliseconds (50));
                timer_.async_wait(std::bind(&printer::print, this));
            }
            else
            {
                std::cout << "Final count is " << count_ << "\n";
                delete this;
            }
        }
        void run() {
            timer_.expires_from_now(std::chrono::milliseconds (50));
            timer_.async_wait(std::bind(&printer::print, this));
            io_.run();
        }
        printer()
            : timer_(io_),
              count_(0) {

        }
        ~printer() {

        }

    public:

        static printer* Create(){
            return new printer;
        }

        void start() {
            std::thread t;
            t = std::thread(std::mem_fn(&printer::run), this);
            t.detach();
        }
};
void foo()
{
    printer *p = printer::Create();
    p->start();
}
int main() {
    foo();
    std::cin.get();
    return 0;
}

输出:

....
490
491
492
493
494
495
496
497
498
499
Final count is 500

1.3、在多线程程序中处理定时回调(多线程处理多个定时任务)
由一个线程来调用io_context::run()导致了回调函数不能够并发的运行。为了避免这个限制,一个直接的方法就是用线程池来调用io_context::run()。然而,为了能够并发的处理回调,还需要一种方法来同步回调对共享的非线程安全的资源的访问。

同一个 io_context 可以同时给多个 timer使下

实例1:

#include <iostream>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>


#include <boost/asio/high_resolution_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_timer.hpp>


class printer2 {
    private:
        boost::asio::steady_timer timer_;
        int count_;
        void print() {
            if (count_ < 10) {
                std::cout << count_ << "\n";
                ++count_;

                timer_.expires_from_now(std::chrono::milliseconds (500));
                timer_.async_wait(std::bind(&printer2::print, this));
            }
            else
            {
                std::cout << "Final count is " << count_ << "\n";
                delete this;
            }
        }
        printer2(boost::asio::io_context &io)
            : timer_(io,std::chrono::milliseconds (500)),
              count_(0) {
                timer_.async_wait(std::bind(&printer2::print, this));

        }
        ~printer2() {

        }

    public:

        static printer2* Create(boost::asio::io_context &io){
            return new printer2(io);
        }

};

int main() {
    boost::asio::io_context io;
    printer2::Create(io);
    printer2::Create(io);
    printer2::Create(io);
    printer2::Create(io);
    io.run();
    //boost::thread t1(boost::bind(&boost::asio::io_context::run, &io));
    std::cin.get();
    return 0;
}

输出;

......
7
7
7
7
8
8
8
8
9
9
9
9
Final count is 10
Final count is 10
Final count is 10
Final count is 10

实例2:

#include <iostream>
#include <chrono>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/system_timer.hpp>

class Timer
{
    public:
        Timer(boost::asio::io_context& io_ctx, boost::asio::io_context::strand& strand_1, unsigned int timeout_, unsigned int id_): id(id_), count(0), timeout(timeout_), t(io_ctx, boost::asio::chrono::milliseconds(timeout_)), strand_(strand_1)
    {
        t.async_wait(boost::asio::bind_executor(strand_, boost::bind(&Timer::OnTimerCallBack, this)));
    }
    private:
        void OnTimerCallBack()
        {
            if(count < 10) {
                ++count;
                std::cout << " Id:" << id << " Count:" << count << std::endl;
                t.expires_at(t.expiry() + boost::asio::chrono::milliseconds(timeout));
                t.async_wait(boost::asio::bind_executor(strand_, boost::bind(&Timer::OnTimerCallBack, this)));
            }
        }
    private:
        unsigned int id;
        unsigned int count;
        unsigned int timeout;
        boost::asio::steady_timer t;
        boost::asio::io_context::strand& strand_;
};

int main()
{
    boost::asio::io_context io_ctx;
    boost::asio::io_context::strand strand_(io_ctx);
    Timer timer1(io_ctx, strand_, 1000, 1);
    Timer timer2(io_ctx, strand_, 1000, 2);
    Timer timer3(io_ctx, strand_, 1000, 3);
    boost::thread t1(boost::bind(&boost::asio::io_context::run, &io_ctx));
    boost::thread t2(boost::bind(&boost::asio::io_context::run, &io_ctx));
    t1.join();
    t2.join();

    return 0;
}

1.4、循环执行定时任务

//boost::posix_time::to_simple_string函数需要这两个头文件
#include <boost/date_time.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
//使用boost.chrono代替std.chrono,
#define BOOST_ASIO_DISABLE_STD_CHRONO
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/thread.hpp>

class Timer
{
public:
    Timer() :work_(io_), timer_(io_){}
public:
    boost::thread_group thgp_;
    boost::asio::io_context io_;
    boost::asio::io_context::work work_;
    boost::asio::steady_timer timer_;
public:
    void init()
    {
        boost::system::error_code errCode;
        thgp_.create_thread(boost::bind(&boost::asio::io_service::run, boost::ref(io_), errCode));
        timer_.expires_from_now(boost::chrono::milliseconds(1000)); //设置过期时间长度
        timer_.async_wait(boost::bind(&Timer::excuteMission, this, boost::asio::placeholders::error));//异步等待
        std::cout << "initialize:" << localTime() << std::endl;
        //由Console可知, 函数立即返回了, 定时器的expires_from_now是由完成端口处理的
    }
    void stop()
    {
        timer_.cancel();  // 取消所有handler
        work_.~work();
        thgp_.join_all();
        std::cout << "Stop:" << localTime() << std::endl;
    }
    static std::string localTime()
    {
        return boost::posix_time::to_simple_string(boost::posix_time::microsec_clock::local_time());
    }

    void excuteMission(const boost::system::error_code& ec)
    {
        std::cout<<"mission to print time:"<<localTime().c_str()<<" ErrorValue:"<<ec.value()<<" ErrorCode:"<<ec.message().c_str()<<std::endl;
        timer_.expires_from_now(boost::chrono::milliseconds(1000));
        timer_.async_wait(boost::bind(&Timer::excuteMission, boost::ref(*this), _1));
#if 0
        timer_.async_wait(boost::bind(&Timer::excuteMission, this, _1));
        timer_.async_wait(boost::bind(&Timer::excuteMission, this, boost::asio::placeholders::error));
#endif
    }
};

int main(int argc, char** argv)
{
    Timer t;
    t.init();
    while(true)
    {
        std::cout<<"execute other mission"<<std::endl;
        boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
    }
    t.stop();
    std::cout << "press ENTER to exit..." << std::endl;
    //    std::cin.sync();
    return 0;
}

2、实现一个定时器执行定时任务

定时器一般支持单线程就够了,一般使用方法见下面代码。如果需要多线程怎么办,笔者一般用一个简单的办法:多线程的业务线程中不包含定时器管理器,单独启一个线程用来管理所有定时器,当时间触发时,向业务线程投递定时器消息即可。

2.1.最小堆实现

头文件min_heap.h

#ifndef TIMERMANAGER_H
#define TIMERMANAGER_H

#include <vector>
#include <boost/function.hpp>
namespace MinHeap {

class TimerManager;

class Timer
{
public:
    enum TimerType { ONCE, CIRCLE };

    Timer(TimerManager& manager);
    ~Timer();

    template<typename Fun>
    void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);
    void Stop();

private:
    void OnTimer(unsigned long long now);

private:
    friend class TimerManager;
    TimerManager& manager_;
    TimerType timerType_;
    boost::function<void(void)> timerFun_;
    unsigned interval_;
    unsigned long long expires_;

    size_t heapIndex_;
};

class TimerManager
{
public:
    static unsigned long long GetCurrentMillisecs();
    void DetectTimers();
private:
    friend class Timer;
    void AddTimer(Timer* timer);
    void RemoveTimer(Timer* timer);

    void UpHeap(size_t index);
    void DownHeap(size_t index);
    void SwapHeap(size_t, size_t index2);

private:
    struct HeapEntry
    {
        unsigned long long time;
        Timer* timer;
    };
    std::vector<HeapEntry> heap_;
};

template<typename Fun>
void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{
    Stop();
    interval_ = interval;
    timerFun_ = fun;
    timerType_ = timeType;
    expires_ = interval_ + TimerManager::GetCurrentMillisecs();
    manager_.AddTimer(this);
}

}
#endif // TIMERMANAGER_H

源文件min_heap.cpp

#include "min_heap.h"
#define _CRT_SECURE_NO_WARNINGS
# include <sys/time.h>

namespace MinHeap {

Timer::Timer(TimerManager& manager)
    : manager_(manager)
    , heapIndex_(-1)
{
}

Timer::~Timer()
{
    Stop();
}

void Timer::Stop()
{
    if (heapIndex_ != -1)
    {
        manager_.RemoveTimer(this);
        heapIndex_ = -1;
    }
}

void Timer::OnTimer(unsigned long long now)
{
    if (timerType_ == Timer::CIRCLE)
    {
        expires_ = interval_ + now;
        manager_.AddTimer(this);
    }
    else
    {
        heapIndex_ = -1;
    }
    timerFun_();
}

// TimerManager
void TimerManager::AddTimer(Timer* timer)
{
    timer->heapIndex_ = heap_.size();
    HeapEntry entry = { timer->expires_, timer };
    heap_.push_back(entry);
    UpHeap(heap_.size() - 1);
}

void TimerManager::RemoveTimer(Timer* timer)
{
    size_t index = timer->heapIndex_;
    if (!heap_.empty() && index < heap_.size())
    {
        if (index == heap_.size() - 1)
        {
            heap_.pop_back();
        }
        else
        {
            SwapHeap(index, heap_.size() - 1);
            heap_.pop_back();
            size_t parent = (index - 1) / 2;
            if (index > 0 && heap_[index].time < heap_[parent].time)
                UpHeap(index);
            else
                DownHeap(index);
        }
    }
}

void TimerManager::DetectTimers()
{
    unsigned long long now = GetCurrentMillisecs();

    while (!heap_.empty() && heap_[0].time <= now)
    {
        Timer* timer = heap_[0].timer;
        RemoveTimer(timer);
        timer->OnTimer(now);
    }
}

void TimerManager::UpHeap(size_t index)
{
    size_t parent = (index - 1) / 2;
    while (index > 0 && heap_[index].time < heap_[parent].time)
    {
        SwapHeap(index, parent);
        index = parent;
        parent = (index - 1) / 2;
    }
}

void TimerManager::DownHeap(size_t index)
{
    size_t child = index * 2 + 1;
    while (child < heap_.size())
    {
        size_t minChild = (child + 1 == heap_.size() || heap_[child].time < heap_[child + 1].time)
            ? child : child + 1;
        if (heap_[index].time < heap_[minChild].time)
            break;
        SwapHeap(index, minChild);
        index = minChild;
        child = index * 2 + 1;
    }
}

void TimerManager::SwapHeap(size_t index1, size_t index2)
{
    HeapEntry tmp = heap_[index1];
    heap_[index1] = heap_[index2];
    heap_[index2] = tmp;
    heap_[index1].timer->heapIndex_ = index1;
    heap_[index2].timer->heapIndex_ = index2;
}


unsigned long long TimerManager::GetCurrentMillisecs()
{
#ifdef _MSC_VER
    _timeb timebuffer;
    _ftime(&timebuffer);
    unsigned long long ret = timebuffer.time;
    ret = ret * 1000 + timebuffer.millitm;
    return ret;
#else
    timeval tv;
    ::gettimeofday(&tv, 0);
    unsigned long long ret = tv.tv_sec;
    return ret * 1000 + tv.tv_usec / 1000;
#endif
}

}

2.2、时间轮实现

头文件timer_wheel.h

#ifndef TIMERWHEEL_H
#define TIMERWHEEL_H
#pragma once
#include <list>
#include <vector>
#include <boost/function.hpp>

namespace TimerWheel{

class TimerManager;

class Timer
{
public:
    enum TimerType {ONCE, CIRCLE};

    Timer(TimerManager& manager);
    ~Timer();

    template<typename Fun>
    void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);
    void Stop();

private:
    void OnTimer(unsigned long long now);

private:
    friend class TimerManager;

    TimerManager& manager_;
    TimerType timerType_;
    boost::function<void(void)> timerFun_;
    unsigned interval_;
    unsigned long long expires_;

    int vecIndex_;
    std::list<Timer*>::iterator itr_;
};

class TimerManager
{
public:
    TimerManager();

    static unsigned long long GetCurrentMillisecs();
    void DetectTimers();

private:
    friend class Timer;
    void AddTimer(Timer* timer);
    void RemoveTimer(Timer* timer);

    int Cascade(int offset, int index);

private:
    typedef std::list<Timer*> TimeList;
    std::vector<TimeList> tvec_;
    unsigned long long checkTime_;
};

template<typename Fun>
inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{
    Stop();
    interval_ = interval;
    timerFun_ = fun;
    timerType_ = timeType;
    expires_ = interval_ + TimerManager::GetCurrentMillisecs();
    manager_.AddTimer(this);
}
}
#endif // TIMERWHEEL_H

源文件timer_wheel.cpp

#include "timer_wheel.h"
#define _CRT_SECURE_NO_WARNINGS
# include <sys/time.h>
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
#define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE)
#define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK)

namespace TimerWheel{
Timer::Timer(TimerManager& manager)
    : manager_(manager)
    , vecIndex_(-1)
{
}

Timer::~Timer()
{
    Stop();
}

void Timer::Stop()
{
    if (vecIndex_ != -1)
    {
        manager_.RemoveTimer(this);
        vecIndex_ = -1;
    }
}

void Timer::OnTimer(unsigned long long now)
{
    if (timerType_ == Timer::CIRCLE)
    {
        expires_ = interval_ + now;
        manager_.AddTimer(this);
    }
    else
    {
        vecIndex_ = -1;
    }
    timerFun_();
}

// TimerManager
TimerManager::TimerManager()
{
    tvec_.resize(TVR_SIZE + 4 * TVN_SIZE);
    checkTime_ = GetCurrentMillisecs();
}

void TimerManager::AddTimer(Timer* timer)
{
    unsigned long long expires = timer->expires_;
    unsigned long long idx = expires - checkTime_;

    if (idx < TVR_SIZE)
    {
        timer->vecIndex_ = expires & TVR_MASK;
    }
    else if (idx < 1 << (TVR_BITS + TVN_BITS))
    {
        timer->vecIndex_ = OFFSET(0) + INDEX(expires, 0);
    }
    else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS))
    {
        timer->vecIndex_ = OFFSET(1) + INDEX(expires, 1);
    }
    else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS))
    {
        timer->vecIndex_ = OFFSET(2) + INDEX(expires, 2);
    }
    else if ((long long) idx < 0)
    {
        timer->vecIndex_ = checkTime_ & TVR_MASK;
    }
    else
    {
        if (idx > 0xffffffffUL)
        {
            idx = 0xffffffffUL;
            expires = idx + checkTime_;
        }
        timer->vecIndex_ = OFFSET(3) + INDEX(expires, 3);
    }

    TimeList& tlist = tvec_[timer->vecIndex_];
    tlist.push_back(timer);
    timer->itr_ = tlist.end();
    --timer->itr_;
}

void TimerManager::RemoveTimer(Timer* timer)
{
    TimeList& tlist = tvec_[timer->vecIndex_];
    tlist.erase(timer->itr_);
}

void TimerManager::DetectTimers()
{
    unsigned long long now = GetCurrentMillisecs();
    while (checkTime_ <= now)
    {
        int index = checkTime_ & TVR_MASK;
        if (!index &&
            !Cascade(OFFSET(0), INDEX(checkTime_, 0)) &&
            !Cascade(OFFSET(1), INDEX(checkTime_, 1)) &&
            !Cascade(OFFSET(2), INDEX(checkTime_, 2)))
        {
            Cascade(OFFSET(3), INDEX(checkTime_, 3));
        }
        ++checkTime_;

        TimeList& tlist = tvec_[index];
        TimeList temp;
        temp.splice(temp.end(), tlist);
        for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr)
        {
            (*itr)->OnTimer(now);
        }
    }
}

int TimerManager::Cascade(int offset, int index)
{
    TimeList& tlist = tvec_[offset + index];
    TimeList temp;
    temp.splice(temp.end(), tlist);

    for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr)
    {
        AddTimer(*itr);
    }

    return index;
}

unsigned long long TimerManager::GetCurrentMillisecs()
{
#ifdef _MSC_VER
    _timeb timebuffer;
    _ftime(&timebuffer);
    unsigned long long ret = timebuffer.time;
    ret = ret * 1000 + timebuffer.millitm;
    return ret;
#else
    timeval tv;
    ::gettimeofday(&tv, 0);
    unsigned long long ret = tv.tv_sec;
    return ret * 1000 + tv.tv_usec / 1000;
#endif
}
}

2.3 定时器应用

main.cpp

#include <iostream>
#include <thread>
#include <chrono>
#include "timer_wheel.h"
#include "min_heap.h"
#include <chrono>

void TimerHandler()
{

    std::chrono::steady_clock::duration d =
            std::chrono::steady_clock::now().time_since_epoch();

    std::chrono::microseconds mic = std::chrono::duration_cast<std::chrono::microseconds>(d);

    std::cout << "Timer:"<<mic.count() << std::endl;
}

typedef void (*Function)();

int main()
{
//    MinHeap::TimerManager tm;
//    MinHeap::Timer t(tm);
    TimerWheel::TimerManager tm;
    TimerWheel::Timer t(tm);
    t.Start(&TimerHandler, 1000);
    while (true)
    {
        tm.DetectTimers();
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    std::cin.get();
    return 0;
}

输出:

Timer:6674738409
Timer:6675739056
Timer:6676739783
Timer:6677740959
Timer:6678746422
Timer:6679749721
Timer:6680751169
Timer:6681754799
Timer:6682754395
Timer:6683762516
^CPress <RETURN> to close this window...

参考:

https://www.cnblogs.com/lyqf365/p/4285166.html

https://zhuanlan.zhihu.com/p/31906251

猜你喜欢

转载自blog.csdn.net/sunlin972913894/article/details/106169559