实现一个简单的基于asio的定时器

定时器的精度为秒级,代码如下:

timer.h

#ifndef ASIO_TIMER_H
#define ASIO_TIMER_H
#include <map>
#include <vector>
#include <mutex>
#include <boost/asio/io_service.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/asio/placeholders.hpp>


typedef void(*ProcessFun)(void*);
typedef boost::shared_ptr < boost::asio::deadline_timer> pDeadlineTimer;
struct STimerUnit
{
    int id;
    pDeadlineTimer t;
    ProcessFun fun;
    int seconds;
};

typedef boost::shared_ptr<STimerUnit> TimerUnitPtr;
class CTimer
{
    public:
        CTimer():m_ioWork(m_ioService),m_lID(0)
        {
        }

    public:

        //添加一个定时业务,f为业务处理函数,arg为自定义参数,seconds为超时秒数
        //返回生成的ID
        int AddTimerUnit(ProcessFun f, void* arg, int seconds);
        //每intervalSeconds秒数执行一次 f函数
        int AddTimerIntervalUnit(ProcessFun f, void *arg, int intervalSeconds);
        //删除指定定时器
        void RemoveTimerUnit(int id);
        
        bool TimerisValid(int id);
        void Run();

    private:
        void TimerProcess(int id, void* arg, bool isIntervalTimer,const boost::system::error_code& e);

    private:
        std::map<int, TimerUnitPtr> m_mapTimerUnits;
    private:
        boost::asio::io_service m_ioService;
        boost::asio::io_service::work m_ioWork;

    private:
        std::mutex m_mutexTimerUnit;

    private:
        //分配timer id
        std::vector<int> m_vecTimerUnitIDs;
        unsigned long long m_lID;
};
#endif

timer.cpp

#include "log.h"
#include "asiotimer.h"

int CTimer::AddTimerUnit(ProcessFun f, void* arg, int seconds)
{
    TimerUnitPtr s(new STimerUnit);
    s->seconds = seconds;
    s->t.reset(new boost::asio::deadline_timer(m_ioService, boost::posix_time::seconds(seconds)));
    s->fun = f;

    {
        std::lock_guard<std::mutex> lock(m_mutexTimerUnit);
        m_mapTimerUnits.insert(std::make_pair(++m_lID, s));
        s->t->async_wait(boost::bind(&CTimer::TimerProcess, this, m_lID, arg, false,boost::asio::placeholders::error));
        return m_lID;
    }
}

int CTimer::AddTimerIntervalUnit(ProcessFun f, void *arg, int intervalSeconds)
{
    TimerUnitPtr s(new STimerUnit);
    s->seconds = intervalSeconds;
    s->t.reset(new boost::asio::deadline_timer(m_ioService, boost::posix_time::seconds(intervalSeconds)));
    s->fun = f;

    {
        std::lock_guard<std::mutex> lock(m_mutexTimerUnit);
        m_mapTimerUnits.insert(std::make_pair(++m_lID, s));
        s->t->async_wait(boost::bind(&CTimer::TimerProcess, this, m_lID, arg, true, boost::asio::placeholders::error));
        return m_lID;
    }
}

void CTimer::RemoveTimerUnit(int id)
{
    std::lock_guard<std::mutex> lock(m_mutexTimerUnit);
    std::map<int, TimerUnitPtr>::iterator It = m_mapTimerUnits.find(id);
    if (It != m_mapTimerUnits.end())
    {
        It->second->t->cancel();
        m_mapTimerUnits.erase(It);
        return;
    }
}

bool CTimer::TimerisValid(int id)
{
    std::lock_guard<std::mutex> lock(m_mutexTimerUnit);
    std::map<int, TimerUnitPtr>::iterator It = m_mapTimerUnits.find(id);
    if (It != m_mapTimerUnits.end())
    {
        return true;
    }

    return false;
}

void CTimer::Run()
{
    m_ioService.run();
}

void CTimer::TimerProcess(int id, void* arg, bool isIntervalTimer, const boost::system::error_code& e)
{
    if (e == boost::asio::error::operation_aborted)
    {
        return;
    }

    TimerUnitPtr pTimerUnit;

    {
        std::lock_guard<std::mutex> lock(m_mutexTimerUnit);
        std::map<int, TimerUnitPtr>::iterator It = m_mapTimerUnits.find(id);
        if (It != m_mapTimerUnits.end())
        {
            pTimerUnit = It->second;
            if (!isIntervalTimer)
            {
                m_mapTimerUnits.erase(It);
            }
        }

        LOG_INFO << "=========>mapTimerUnits size " << m_mapTimerUnits.size() << std::endl;
    }

    if (pTimerUnit)
    {
        pTimerUnit->fun(arg);
        if (isIntervalTimer)
        {
            pTimerUnit->t->expires_at(pTimerUnit->t->expires_at() + boost::posix_time::seconds(pTimerUnit->seconds));
            pTimerUnit->t->async_wait(boost::bind(&CTimer::TimerProcess, this, id, arg, true, boost::asio::placeholders::error));
        }
    }
    else
    {
        LOG_INFO << "TimerUnit pointer is NULL" << std::endl;
    }
}

main.cpp
void Print(void *arg)
{
    std::cout << "==============>Print "<<*(int*)arg << std::endl;
}

void Print1(void *arg)
{
    std::cout << "================>Print1 " << *(int*)arg << std::endl;
}

void Print2(void *arg)
{
    std::cout << "===================>Print2 " << *(int*)arg << std::endl;
}

int main()
{   
    CTimer t1;
    std::thread t(std::bind(&CTimer::Run, &t1));
    /*int i3 = 3;
    t1.AddTimerUnit(Print2, &i3,3);*/

    //t1.Run();
    int i = 1;
    int id = t1.AddTimerUnit(Print, &i, 5);
    //t1.RemoveTimerUnit(id);
    int i2 = 2;
    t1.AddTimerUnit(Print1, &i2, 2);

    int i3 = 3;
    int ID = t1.AddTimerIntervalUnit(Print2, &i3, 1);
    Sleep(10 * 1000);
    t1.RemoveTimerUnit(ID);
    std::system("pause");
}


1/24号,修改一处问题,如果在AddTimerUnit之前调用Run方法,io_service会因为没有任务而直接退出,添加一个io_service::work 成员变量,让io_service一直保持loop。

 添加了AddTimerIntervalUnit接口,用于在每隔intervalSeconds秒数调用一次业务处理回调。

 修改了TimerProcess函数,在操作定时器之前先用临时指针指向map中存储的定时器对象后,先对map进行删除操作后,再通过临时指针调用设置的业务回调函数。这样做的目的是:如果在TimerProcess函数中直接通过迭代器访问定时器对象来调用业务函数,如:It->second->fun(arg),此时在fun回调函数中再次调用Add函数来添加定位器,此时就会造成迭代器失效,而造成崩溃。



猜你喜欢

转载自blog.csdn.net/mo4776/article/details/79131348