C++11 定时器

my_time.cpp

#include "my_timer.h"

namespace MyCommon
{
    CMyTimer::CMyTimer()
    : m_mutex{}, m_cond{}, m_threadWorker{}, m_vecTask{}, m_msetTaskEvents{}, m_stackFreeIDs{}
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_bNeedStop = false;
        m_threadWorker = std::thread([this] { Run(); });
    }

    CMyTimer::~CMyTimer()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_bNeedStop = true;
        lock.unlock();
        m_cond.notify_all();
        m_threadWorker.join();
        m_vecTask.clear();
        m_msetTaskEvents.clear();
        while(!m_stackFreeIDs.empty()) 
        {
            m_stackFreeIDs.pop();
        }
    }

    size_t CMyTimer::Add(const std::chrono::time_point<std::chrono::steady_clock> &when, std::function<void(size_t)> &&handler, const std::chrono::microseconds &period)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        size_t id = 0;
        // 添加一个新任务,如果freeid池没有id,需要新加入一个id
        if(m_stackFreeIDs.empty()) {
            id = m_vecTask.size();
            TTask task(id, when, period, std::move(handler));
            m_vecTask.push_back(std::move(task));
        } else {
            id = m_stackFreeIDs.top();
            m_stackFreeIDs.pop();
            TTask task(id, when, period, std::move(handler));
            m_vecTask[id] = std::move(task);
        }
        m_msetTaskEvents.insert(TTaskEvent{when, id});
        lock.unlock();
        m_cond.notify_all();
        return id;
    }

    size_t CMyTimer::Add(const uint64_t when, std::function<void(size_t)> &&handler, const uint64_t period )
    {
        return Add(std::chrono::microseconds(when), std::move(handler), std::chrono::microseconds(period));
    }

    bool  CMyTimer::Remove(size_t id)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        if(m_vecTask.size() == 0 || m_vecTask.size() <= id) {
            return false;
        }
        m_vecTask[id].valid = false;
        m_vecTask[id].handler = nullptr;
        auto it = std::find_if(m_msetTaskEvents.begin(), m_msetTaskEvents.end(),
            [&](const TTaskEvent &te) { return te.ref == id; });
        if(it != m_msetTaskEvents.end()) {
            m_stackFreeIDs.push(it->ref);
            m_msetTaskEvents.erase(it);
        }
        lock.unlock();
        m_cond.notify_all();
        return true;
    }

    void CMyTimer::Run()
    {
        std::unique_lock<std::mutex> lock(m_mutex);

        while(!m_bNeedStop) {

            if(m_msetTaskEvents.empty()) {
                // Wait for work
                m_cond.wait(lock);
            } else {
                TTaskEvent te = *m_msetTaskEvents.begin();
                if(std::chrono::steady_clock::now() >= te.next) 
                {

                    // Remove time event
                    m_msetTaskEvents.erase(m_msetTaskEvents.begin());

                    // Invoke the handler
                    lock.unlock();
                    m_vecTask[te.ref].handler(te.ref);
                    lock.lock();

                    if(m_vecTask[te.ref].valid && m_vecTask[te.ref].period.count() > 0) 
                    {
                        // The event is valid and a periodic timer.
                        te.next += m_vecTask[te.ref].period;
                        m_msetTaskEvents.insert(te);
                    } else {
                        // The event is either no longer valid because it was removed in the
                        // callback, or it is a one-shot timer.
                        m_vecTask[te.ref].valid = false;
                        m_vecTask[te.ref].handler = nullptr;
                        m_stackFreeIDs.push(te.ref);
                    }
                } else {
                    /* 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点 */
                    m_cond.wait_until(lock, te.next);
                }
            }
        }
    }
}; //end of namespace MyCommon

my_timer.h

#ifndef  _MY_TIMER_H_
#define  _MY_TIMER_H_

#include <algorithm>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <set>
#include <stack>
#include <thread>
#include <vector>

namespace MyCommon
{
    // The event structure that holds the information about a timer.
    typedef struct TagTask
    {
        size_t id;
        std::chrono::time_point<std::chrono::steady_clock> start;
        std::chrono::microseconds period;
        std::function<void(size_t)> handler;
        bool valid;
        TagTask()
            : id(0), start(std::chrono::microseconds::zero()), period(std::chrono::microseconds::zero()), handler(nullptr), valid(false)
        {
        }

        template <typename Func>
        TagTask(size_t id, std::chrono::time_point<std::chrono::steady_clock> start, std::chrono::microseconds period, Func &&handler)
            : id(id), start(start), period(period), handler(std::forward<Func>(handler)), valid(true)
        {
        }

        TagTask(TagTask &&r) = default;
        TagTask &operator=(TagTask &&ev) = default;
        TagTask(const TagTask &r) = delete;
        TagTask &operator=(const TagTask &r) = delete;
    }TTask;

    // 定时器结构体 ,记录下一次超时的时间点 and a reference to its
    // Event struct.
    typedef struct TagTaskEvent {
        std::chrono::time_point<std::chrono::steady_clock> next;
        size_t ref;

        bool operator<( const TagTaskEvent &r) const
        {
            return this->next < r.next;
        }
    }TTaskEvent;

    class CMyTimer
    {
    public:

    public:
        CMyTimer();
        virtual ~CMyTimer();

        /**
         * 添加一个定时器.
         *
         * \param when 到达该时间点时,开始执行定时器.
         * \param handler 定时器触发时,调用本函数回调.
         * \param period 计时器触发的周期。仅用于周期计时器.
         */
        size_t Add(const std::chrono::time_point<std::chrono::steady_clock> &when, std::function<void(size_t)> &&handler, const std::chrono::microseconds &period = std::chrono::microseconds::zero());


        /**
         * 重载的“add”函数,在第一次超时时使用“std::chrono::duration”而不是“time_point”。
         * 模板类不能分离头文件,所有的代码必须放在一起,编译器不会查找cpp文件,只会在声明方法的文件内查找定义
         */
        template <class Rep, class Period>
        size_t Add(const std::chrono::duration<Rep, Period> &when, std::function<void(size_t)> &&handler, const std::chrono::microseconds &period = std::chrono::microseconds::zero())
        {
            return Add(std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::microseconds>(when),
            std::move(handler), period);
        }

        /**
         * 重载的“add”函数,在第一次超时和期间使用uint64_t而不是“time_point”。
         */
        size_t Add(const uint64_t when, std::function<void(size_t)> &&handler, const uint64_t period = 0);

        /**
         * 根据id移除定时器
         */
        bool Remove(size_t id);

    private:
        void Run();

    private:
        // 线程和锁的变量.
        std::mutex m_mutex;
        std::condition_variable m_cond;
        std::thread m_threadWorker;

        // 需要停止定时器线程.
        bool m_bNeedStop = false;

        // 保存所有活动任务。重复使用,程序退出前不会销毁,因为使用了vector的下标和size作为id
        std::vector<TTask> m_vecTask;

        // 排序集合,顶部有下一个超时的任务
        std::multiset<TTaskEvent> m_msetTaskEvents;

        // id 池.
        std::stack<size_t> m_stackFreeIDs;

    };

};//end of namespace MyCommon
#endif

伪代码demo



class CTest
{
public:
    CTest();
    ~CTest();
    /* 开始 */
    int Start()
    {
        if(!m_pTimer)
        {
            std::unique_ptr<MyCommon::CMyTimer> pTimer(new MyCommon::CMyTimer());
            m_pTimer = std::move(pTimer);
            /* 定时器,1秒钟触发一次 */
            m_nTimerID = m_pTimer->Add(std::chrono::milliseconds(0), CTest::OnRtcpTimer, std::chrono::microseconds(1000000));

            m_pTimer->Add(std::chrono::milliseconds(1000), [&](std::size_t){
            print();
            std::cout<<" ... print ..."<<std::endl;
            },std::chrono::microseconds(1000*1000));
        }
    }

    /* 停止 */
    int Stop()
    {
        if(!m_pTimer)
        {
            m_pTimer->Remove(m_nTimerID);
        }
    }
private:
    static void OnRtcpTimer(size_t id)
    {
        std::cout<<"CMyRtcpManager::OnRtcpTimer ......"<<std::endl;
    }

    void print()
    {
        std::cout<<"CMyRtcpManager::print ......"<<std::endl;
    }


    std::unique_ptr<MyCommon::CMyTimer> m_pTimer;
    /* 定时器ID */
    size_t      m_nTimerID;
}

int main()
{
    CTest test;
    getchar();
    return 0;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值