C++异步定时器设计与实现

C++异步定时器设计与实现

由于目前C++标准中没有现成的定时器,本设计使用C++11相关语法并进行封装。

本定时器包含一个TimerManager类用于创建定时器和进行定时任务管理,TimerManager会创建两个线程(mTimerTickThread、mTimerCallbackThread)分别用于时间处理和函数回调。
可以使用TimerManager的create方法创建多个定时器,每次创建的定时器ID会累加,并返回ITimer类型的共享指针。其中ITimer类中定义了start和stop方法,用于启动或停止当前定时器。
TimerManager还有一个内部类TimerMessageQueue用于实现定时器消息的插入、删除与定时控制。

关键类型及接口介绍

TimerMessage类型作为通信的元素类型

using TimerCallback = std::function<void()>;

struct TimerMessage {
    const int id;                 // time id.
    const long long timeout;      // time out value.
    const TimerCallback callback; // timeout callback function.
    const TimerType type;         // 0:once, 1:periodic.
    long long when;               // time out occure time.
    bool valid;                   // set false when start/stop, set true when time is up.

    TimerMessage()
        : id(0), timeout(0), callback(nullptr), type(ONCE_TIMER), when(0), valid(false)
    {}

    TimerMessage(const int &id, const long long &timeout, const TimerCallback &callback,
                    const TimerType &type = ONCE_TIMER)
        : id(id), timeout(timeout), callback(callback), type(type), when(0), valid(false)
    {}
};

id、timeout、callback、type伴随着timer的生命周期,一旦创建不可修改。
when、valid为辅助信息,在消息流转过程中适时变化。

TimerManager的createTimer方法

TimerRefPtr createTimer(const long long &timeoutMillis, const TimerCallback &callback,
                        const TimerType &type = TIMERTYPE_ONCE)

用于创建定时器,有三个参数,分别是定时时长(单位毫秒),超时callback用于定时器到期的回调函数,定时器类型(单次还是循环定时器)。

定时器类的start、stop方法

bool start(void)void stop(void)

start时会向TimerMessageQueue中插入消息,该消息包括定时器id、超时时间timeout、回调函数callback、定时器类型type、到期时间when,插入时按when进行排序(二分法、复杂度为log2N);
stop时会依据id从TimerMessageQueue中删除消息,并将valid设置为false,防止继续callback;
在重复start时会先stop原来的定时器再重新start,到期时间以最后一次start的时间为准。

核心逻辑介绍

TimerMessageQueue的插入与删除实现,确保消息能高效的按序插入对应位置。

bool enqueueMessage(const TimerMessageRefPtr &message, const long long &when)
{
    do {
        std::lock_guard<std::mutex> lg(mMuxLock);
        mMapIdWhens[message->id].push_back(when);
        LOGD("add message id:%d, when:%lld", message->id, when);
        message->when = when;
        mMessages.insert(
            std::upper_bound(mMessages.begin(), mMessages.end(), when,
                                [](const long long &when, const TimerMessageRefPtr &m) {
                                    return (when < m->when);
                                }),
            message);
    } while (0);

    mCondVar.notify_one();
    std::this_thread::yield();
    return true;
}

void removeMessages(const int &id)
{
    std::lock_guard<std::mutex> lg(mMuxLock);
    auto it = mMapIdWhens.find(id);
    if (it == mMapIdWhens.end()) {
        return;
    }
    for (auto const &when : it->second) {
        LOGD("del message id:%d, when:%lld", id, when);
        mMessages.erase(
            std::remove_if(
                std::lower_bound(mMessages.begin(), mMessages.end(), when,
                                    [](const TimerMessageRefPtr &m, const long long &when) {
                                        return (m->when < when);
                                    }),
                std::upper_bound(mMessages.begin(), mMessages.end(), when,
                                    [](const long long &when, const TimerMessageRefPtr &m) {
                                        return (when < m->when);
                                    }),
                [&id](const TimerMessageRefPtr &m) { return ((id == m->id)); }),
            mMessages.end());
    }
    mMapIdWhens.erase(id);
}

bool hasMessages(const int &id) const
{
    std::lock_guard<std::mutex> lg(mMuxLock);
    bool ret = (mMapIdWhens.end() != mMapIdWhens.find(id));
    LOGV("has message id:%d %s", id, ret ? "yes" : "no");
    return ret;
}

std::lower_bound和std::upper_bound用于在有序的容器中快速查找可插入位置(不小于目标值和不大于目标值的位置)迭代器。

取出定时器到期消息

mTimerTickThread用于从mTimerTickQueue中取出到期的定时器消息。
如果没有到期消息则会休眠等待,等待的时间是最近要到期的时间与当前时间差,并且可以随时被打断(当有新的消息插入时会打断);
由于消息插入时是按到期时间排序插入的,所以每次取出的都是最近要到期的那一条定时消息。

inline void waitForItems(void)
{
    std::unique_lock<std::mutex> ul(mMuxLock);
    mCondVar.wait(ul, [this]() { return !mMessages.empty(); });
    if (0 == mMessages.front()->when) {
        return;
    }

    long long waitingTimeMicros = 0;
    while (!mMessages.empty()
            && (waitingTimeMicros = mMessages.front()->when - steadyTimeMicros()) > 0) {
        // next message is not ready, set a timeout to wake up when it is ready.
        mCondVar.wait_for(ul, std::chrono::microseconds(waitingTimeMicros));
    }
}

inline TimerMessageRefPtr next(void)
{
    while (true) {
        waitForItems();
        do {
            std::lock_guard<std::mutex> lg(mMuxLock); // minimise the lock scope.
            if (!mMessages.empty()
                && (steadyTimeMicros() >= mMessages.front()->when)) { // time is up.
                TimerMessageRefPtr msg = std::move(mMessages.front());
                mMessages.pop_front();
                msg->valid = true;

                // remove message by id and when from map.
                auto &whens = mMapIdWhens[msg->id];
                whens.erase(std::remove_if(whens.begin(), whens.end(),
                                            [&msg](const long long &when) {
                                                return when == msg->when;
                                            }),
                            whens.end());
                if (whens.empty()) {
                    mMapIdWhens.erase(msg->id);
                }

                LOGD("pop message id:%d, when:%lld", msg->id, msg->when);
                return msg;
            }
        } while (0);
    }
}

处理定时器到期消息

mTimerTickThread从mTimerTickQueue中取出到期的定时器消息后,再插入mCallbackQueue中,供mTimerCallbackThread线程调用回调函数完成定时器超时回调通知。
如果是循环定时器,则将定时消息的到期时间when加上超时时间timeout后重新插入mTimerTickQueue中,继续进行对应消息的流转。

do {
    auto message = mTimerTickQueue->next();
    if (!message) {
        continue;
    }
    if (!message->callback) {
        // No callback is a magic identifier for the quit message.
        break;
    }
    do {
        std::unique_lock<std::mutex> ul(mMutexLock);
        if (!message->valid) {
            break;
        }
        if (PERIODIC_TIMER == message->type) {
            (void)mTimerTickQueue->enqueueMessage(message,
                                                    message->when + message->timeout);
        }

        (void)mCallbackQueue->push_back(message);
        ul.unlock();

        mCondVar.notify_one();
        std::this_thread::yield();
    } while (0);
} while (mMainThreadAlive);

回调定时器回调函数

mTimerCallbackThread线程从mCallbackQueue中取出消息并调用回调函数完成定时器超时回调通知。

TimerMessageRefPtr message = nullptr;
do {
    std::unique_lock<std::mutex> ul(mMutexLock);
    mCondVar.wait(ul, [this]() { return !mCallbackQueue->empty(); });

    message = mCallbackQueue->front();
    mCallbackQueue->pop_front();
    if (!message) {
        continue;
    }
    if (!message->callback) {
        // No callback is a magic identifier for the quit message.
        break;
    }
    if (!message->valid) {
        continue;
    }

    // handler dispatch message.
    LOGV("callback message id:%d, when:%lld", message->id, message->when);
    message->callback();
    ul.unlock();

    std::this_thread::yield();
} while (mMainThreadAlive);

使用步骤举例

  1. create timer manager instance
mdtimer::TimerManagerRefPtr timerMgr = mdtimer::TimerManager::getInstance();
  1. create timer instance
void timeoutCallbackNoArgs()
{
    LOGI("timeoutCallbackNoArgs without any parameter.");
}

void timeoutCallback1Args(const std::string &info)
{
    LOGI("timeoutCallback1Args parameter:%s", info.c_str());
}

mdtimer::TimerRefPtr timer0 = timerMgr->createTimer(50, timeoutCallbackNoArgs);
auto timer1 = timerMgr->createTimer(50, []() { LOGI("once timer1, 50ms function callback."); });
auto timer2 = timerMgr->createTimer(50, []() { timeoutCallback1Args("once timer2, 50ms"); });
auto timer3 = timerMgr->createTimer(50, std::bind(timeoutCallback1Args, "once timer3, 50ms"));
auto timer4 = timerMgr->createTimer(100, std::bind(timeoutCallback1Args, "periodic timer4, 100ms"),
                                    mdtimer::PERIODIC_TIMER);
  1. start timer
(void)timer1->start();
(void)timer2->start();
  1. stop timer
timer1->stop();
timer2->stop();

参考代码

定时器实现

/*
 * Copyright (C) 2023 . All rights reserved.
 *
 * File name   : MDTimer.hpp
 * Author      : longbin
 * Created date: 2020-05-07 21:54:42
 * Description : message driven timer
 *
 */
#ifndef __MDTIMER_HPP__
#define __MDTIMER_HPP__

#include <iostream>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <functional>
#include <chrono>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <condition_variable>
#include <algorithm>

#ifdef LOG_TAG
#    undef LOG_TAG
#endif // LOG_TAG
#define LOG_TAG "MDTimer"
#include "logging.hpp"

// usage:
// 1. create timer manager instance;
// mdtimer::TimerManagerRefPtr timerMgr = mdtimer::TimerManager::getInstance();
// 2. create timer instance;
//
// void timeoutCallbackNoArgs()
// {
//     LOGI("timeoutCallbackNoArgs without any parameter.");
// }
//
// void timeoutCallback1Args(const std::string &info)
// {
//     LOGI("timeoutCallback1Args parameter:%s", info.c_str());
// }
//
// mdtimer::TimerRefPtr timer0 = timerMgr->createTimer(50, timeoutCallbackNoArgs);
// auto timer1 = timerMgr->createTimer(50, []() { LOGI("once timer1, 50ms function callback."); });
// auto timer2 = timerMgr->createTimer(50, []() { timeoutCallback1Args("once timer2, 50ms"); });
// auto timer3 = timerMgr->createTimer(50, std::bind(timeoutCallback1Args, "once timer3, 50ms"));
// auto timer4 = timerMgr->createTimer(100, std::bind(timeoutCallback1Args, "periodic timer4,
//                                     100ms"), mdtimer::PERIODIC_TIMER);
// 3. start timer;
// (void)timer1->start();
// (void)timer2->start();
// 4. stop timer;
// timer1->stop();
// timer2->stop();
// 5. can start/stop again if needed for once/periodic timer.
// (void)timer1->start();
// timer2->stop();
// note that, the timer can stop even if it hasnt started.

namespace mdtimer
{

using TimerType = enum TimerType { ONCE_TIMER = 0, PERIODIC_TIMER = 1 };
using TimerCallback = std::function<void()>;

class TimerManager;
using TimerManagerRefPtr = std::shared_ptr<TimerManager>;

class ITimer;
using TimerRefPtr = std::shared_ptr<ITimer>;

class ITimer
{
public:
    virtual ~ITimer() = default;

    virtual bool start(void) = 0;
    virtual void stop(void) = 0;
}; // class ITimer

class TimerManager : public std::enable_shared_from_this<TimerManager>
{
private:
    class TimerMessage;
    using TimerMessageRefPtr = std::shared_ptr<TimerMessage>;
    using TimerMessageRefPtrList = std::deque<TimerMessageRefPtr>;

    struct TimerMessage {
        const int id;                 // time id.
        const long long timeout;      // timeout value.
        const TimerCallback callback; // timeout callback function.
        const TimerType type;         // timer type: 0-once, 1-periodic.
        long long when;               // timeout occure time.
        bool valid;                   // set false when start/stop, set true when time is up.

        TimerMessage()
            : id(0), timeout(0), callback(nullptr), type(ONCE_TIMER), when(0), valid(false)
        {}

        TimerMessage(const int &id, const long long &timeout, const TimerCallback &callback,
                     const TimerType &type = ONCE_TIMER)
            : id(id), timeout(timeout), callback(callback), type(type), when(0), valid(false)
        {}
    };

    class TimerMessageQueue
    {
    private:
        constexpr static bool mDoesShowMessages = false;

        TimerMessageRefPtrList mMessages{};
        std::map<int, std::list<long long>> mMapIdWhens;
        mutable std::mutex mMuxLock;
        std::condition_variable mCondVar;

    private:
        TimerMessageQueue(const TimerMessageQueue &) = delete;
        TimerMessageQueue &operator=(const TimerMessageQueue &) = delete;

        inline void showMessages()
        {
            std::string queue{};
            std::for_each(mMessages.begin(), mMessages.end(),
                          [&queue](const TimerMessageRefPtr &m) {
                              queue += std::to_string(m->id) + ":" + std::to_string(m->when) + " ";
                          });
            LOGD("show messages (size:%zu) %s", mMessages.size(), queue.c_str());
        }

        inline void waitForItems(void)
        {
            std::unique_lock<std::mutex> ul(mMuxLock);
            mCondVar.wait(ul, [this]() { return !mMessages.empty(); });
            if (0 == mMessages.front()->when) {
                return;
            }

            long long waitingTimeMicros = 0;
            while (!mMessages.empty()
                   && (waitingTimeMicros = mMessages.front()->when - steadyTimeMicros()) > 0) {
                // next message is not ready, set a timeout to wake up when it is ready.
                mCondVar.wait_for(ul, std::chrono::microseconds(waitingTimeMicros));
            }
        }

    public:
        TimerMessageQueue() = default;
        virtual ~TimerMessageQueue() = default;

        TimerMessageRefPtr next(void)
        {
            while (true) {
                waitForItems();
                do {
                    // minimise the lock scope.
                    std::lock_guard<std::mutex> lg(mMuxLock);
                    if (!mMessages.empty() && (steadyTimeMicros() >= mMessages.front()->when)) {
                        // time is up.
                        TimerMessageRefPtr msg = std::move(mMessages.front());
                        mMessages.pop_front();
                        msg->valid = true;

                        // remove message by id and when from map.
                        auto &whens = mMapIdWhens[msg->id];
                        whens.erase(std::remove_if(whens.begin(), whens.end(),
                                                   [&msg](const long long &when) {
                                                       return when == msg->when;
                                                   }),
                                    whens.end());
                        if (whens.empty()) {
                            mMapIdWhens.erase(msg->id);
                        }

                        LOGD("pop message id:%d, when:%lld", msg->id, msg->when);
                        return msg;
                    }
                } while (0);
            }
        }

        bool enqueueMessage(const TimerMessageRefPtr &message, const long long &when)
        {
            do {
                std::lock_guard<std::mutex> lg(mMuxLock);
                mMapIdWhens[message->id].push_back(when);
                LOGD("add message id:%d, when:%lld", message->id, when);
                message->when = when;

                mMessages.insert(
                    std::upper_bound(mMessages.begin(), mMessages.end(), when,
                                     [](const long long &when, const TimerMessageRefPtr &m) {
                                         return (when < m->when);
                                     }),
                    message);

                if (mDoesShowMessages) {
                    showMessages();
                }
            } while (0);

            mCondVar.notify_one();
            std::this_thread::yield();
            return true;
        }

        void removeMessages(const int &id)
        {
            std::lock_guard<std::mutex> lg(mMuxLock);
            auto it = mMapIdWhens.find(id);
            if (it == mMapIdWhens.end()) {
                return;
            }

            for (auto const &when : it->second) {
                LOGD("del message id:%d, when:%lld", id, when);
                mMessages.erase(
                    std::remove_if(
                        std::lower_bound(mMessages.begin(), mMessages.end(), when,
                                         [](const TimerMessageRefPtr &m, const long long &when) {
                                             return (m->when < when);
                                         }),
                        std::upper_bound(mMessages.begin(), mMessages.end(), when,
                                         [](const long long &when, const TimerMessageRefPtr &m) {
                                             return (when < m->when);
                                         }),
                        [&id](const TimerMessageRefPtr &m) { return ((id == m->id)); }),
                    mMessages.end());
            }
            mMapIdWhens.erase(id);

            if (mDoesShowMessages) {
                showMessages();
            }
        }

        bool hasMessages(const int &id) const
        {
            std::lock_guard<std::mutex> lg(mMuxLock);
            bool ret = (mMapIdWhens.end() != mMapIdWhens.find(id));
            LOGV("has message id:%d %s", id, ret ? "yes" : "no");
            return ret;
            // return mMessages.end()
            //        != std::find_if(mMessages.begin(), mMessages.end(),
            //                        [&id](const TimerMessageRefPtr &m) { return ((id == m->id));
            //                        });
        }
    }; // class TimerMessageQueue

    class TimerImpl : public ITimer
    {
        friend class TimerManager; // to access its private start/stop
    private:
        TimerManagerRefPtr mTimerManager = nullptr;
        TimerMessageRefPtr mTimerMessage = nullptr;

    public:
        explicit TimerImpl(const TimerManagerRefPtr &manager, const int &id,
                           const long long &timeoutMillis, const TimerCallback &callback,
                           const TimerType &type)
        {
            LOGI("create timer id:%d, timeout:%lldms, type:%d", id, timeoutMillis, type);
            mTimerManager = manager;
            mTimerMessage =
                std::make_shared<TimerMessage>(id, timeoutMillis * 1000, callback, type);
        }

        virtual ~TimerImpl()
        {
            LOGI("destroy timer id:%d", mTimerMessage->id);
            this->stop(); // if destroy timer manager before timer the stop will be blocked.
        }

        virtual bool start(void) override
        {
            mTimerMessage->when = mTimerMessage->timeout + steadyTimeMicros();
            return mTimerManager ? mTimerManager->start(mTimerMessage) : false;
        }

        virtual void stop(void) override
        {
            if (mTimerManager) {
                mTimerManager->stop(mTimerMessage);
            }
        }
    }; // class TimerImpl

public:
    virtual ~TimerManager()
    {
        LOGD("~TimerManager");
        mMainThreadAlive = false;

        (void)mCallbackQueue->push_back(std::make_shared<TimerMessage>());
        mCondVar.notify_all();

        (void)mTimerTickQueue->enqueueMessage(std::make_shared<TimerMessage>(), 0);
        (void)mTimerTickQueue->enqueueMessage(std::make_shared<TimerMessage>(), -1);

        if (mTimerTickThread.joinable()) {
            mTimerTickThread.join();
        }
        if (mTimerCallbackThread.joinable()) {
            mTimerCallbackThread.join();
        }
    }

    static TimerManagerRefPtr getInstance(void)
    {
        static TimerManagerRefPtr instance = TimerManagerRefPtr(new TimerManager());
        return instance;
    }

    TimerRefPtr createTimer(const long long &timeoutMillis, const TimerCallback &callback,
                            const TimerType &type = ONCE_TIMER)
    {
        int id = mTimerId.fetch_add(1);
        return TimerRefPtr(new TimerImpl(shared_from_this(), id, timeoutMillis, callback, type));
    }

    static long long steadyTimeMicros(void)
    {
        auto now = std::chrono::time_point_cast<std::chrono::microseconds>(
            std::chrono::steady_clock::now());
        std::chrono::microseconds span =
            std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch());
        return span.count();
    }

private:
    TimerManager(const TimerManager &) = delete;
    TimerManager &operator=(const TimerManager &) = delete;

    TimerManager() : mMainThreadAlive(true)
    {
        mTimerTickThread = std::thread([this]() {
            do {
                auto message = mTimerTickQueue->next();
                if (!message) {
                    continue;
                }
                if (!message->callback) {
                    // exit loop
                    // No callback is a magic identifier for the quit message.
                    break;
                }

                do {
                    std::unique_lock<std::mutex> ul(mMutexLock);
                    if (!message->valid) {
                        break;
                    }
                    if (PERIODIC_TIMER == message->type) {
                        (void)mTimerTickQueue->enqueueMessage(message,
                                                              message->when + message->timeout);
                    }

                    (void)mCallbackQueue->push_back(message);
                    ul.unlock();

                    mCondVar.notify_one();
                    std::this_thread::yield();
                } while (0);
            } while (mMainThreadAlive);

            LOGI("TimerTickThread exit.");
        });

        mTimerCallbackThread = std::thread([this]() {
            TimerMessageRefPtr message = nullptr;
            do {
                std::unique_lock<std::mutex> ul(mMutexLock);
                mCondVar.wait(ul, [this]() { return !mCallbackQueue->empty(); });

                message = mCallbackQueue->front();
                mCallbackQueue->pop_front();
                if (!message) {
                    continue;
                }
                if (!message->callback) {
                    // eixit loop
                    // No callback is a magic identifier for the quit message.
                    break;
                }
                if (!message->valid) {
                    continue;
                }

                // handler dispatch message.
                LOGV("callback message id:%d, when:%lld", message->id, message->when);
                message->callback();
                ul.unlock();

                std::this_thread::yield();
            } while (mMainThreadAlive);

            LOGI("TimerCallbackThread exit.");
        });
    }

    inline bool start(const TimerMessageRefPtr &message)
    {
        std::lock_guard<std::mutex> lg(mMutexLock);
        LOGI("start timer id:%d, timeout:%lldms", message->id, message->timeout / 1000);

        // stop the exist timer then start.
        if (mTimerTickQueue->hasMessages(message->id)) {
            mTimerTickQueue->removeMessages(message->id);
        }
        mCallbackQueue->erase(std::remove_if(mCallbackQueue->begin(), mCallbackQueue->end(),
                                             [&message](const TimerMessageRefPtr &m) {
                                                 return ((message->id == m->id));
                                             }),
                              mCallbackQueue->end());
        message->valid = false;

        return mTimerTickQueue->enqueueMessage(message, message->when);
    }

    inline void stop(const TimerMessageRefPtr &message)
    {
        std::lock_guard<std::mutex> lg(mMutexLock);
        LOGI("stop timer id:%d", message->id);

        if (mTimerTickQueue->hasMessages(message->id)) {
            mTimerTickQueue->removeMessages(message->id);
        }
        mCallbackQueue->erase(std::remove_if(mCallbackQueue->begin(), mCallbackQueue->end(),
                                             [&message](const TimerMessageRefPtr &m) {
                                                 return ((message->id == m->id));
                                             }),
                              mCallbackQueue->end());
        message->valid = false;
    }

private:
    std::atomic<int> mTimerId{1};
    std::shared_ptr<TimerMessageQueue> mTimerTickQueue = std::make_shared<TimerMessageQueue>();
    std::shared_ptr<TimerMessageRefPtrList> mCallbackQueue =
        std::make_shared<TimerMessageRefPtrList>();

    std::atomic<bool> mMainThreadAlive{true};
    std::thread mTimerTickThread;
    std::thread mTimerCallbackThread;

    mutable std::mutex mMutexLock;
    std::condition_variable mCondVar;
}; // class TimerManager

};     // namespace mdtimer
#endif //__MDTIMER_HPP__

定时器使用例子

/*****************************************
 * Copyright (C) 2020 * Ltd. All rights reserved.
 * File name   : mdtimertest.cpp
 * Created date: 2020-05-07 00:35:00
 *******************************************/

#include <iostream>
#include <string>
#include <memory>

#include <unistd.h>
#include <signal.h>

#include "MDTimer.hpp"

#ifdef LOG_TAG
#    undef LOG_TAG
#endif // LOG_TAG
#define LOG_TAG "MDTimerTest"
#include "logging.hpp"

void timeoutCallbackNoArgs()
{
    LOGI("timeoutCallbackNoArgs.");
}

void timeoutCallback1Args(const std::string &info)
{
    LOGI("timeoutCallback1Args info:%s", info.c_str());
}

void signalHandler(int signo)
{
    LOGE("caught signal: %d", signo);
    // callstack_dump("CALLSTACK");
    /* reset signal handle to default */
    (void)signal(signo, SIG_DFL);
    /* will receive SIGSEGV again and exit app */
}

int main()
{
    (void)signal(SIGINT, signalHandler);
    (void)signal(SIGSEGV, signalHandler);
    (void)signal(SIGABRT, signalHandler);

    mdtimer::TimerManagerRefPtr timerMgr = mdtimer::TimerManager::getInstance();
    if (timerMgr == nullptr) {
        LOGE("ERROR: create timer manager failed.");
        return -1;
    }

    mdtimer::TimerRefPtr timer0 = timerMgr->createTimer(50, timeoutCallbackNoArgs);
    (void)timer0->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    timer0->stop();

    auto timer1 = timerMgr->createTimer(50, []() { LOGI("once timer1, 50ms function callback."); });
    (void)timer1->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    timer1->stop();
    (void)timer1->start(); // can start again
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    timer1->stop();

    auto timer2 = timerMgr->createTimer(50, []() { timeoutCallback1Args("once timer2, 50ms"); });
    (void)timer2->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    timer2->stop();

    auto timer3 = timerMgr->createTimer(50, std::bind(timeoutCallback1Args, "once timer3, 50ms"));
    (void)timer3->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    timer3->stop();

    // periodic timer
    auto timer4 = timerMgr->createTimer(
        100, std::bind(timeoutCallback1Args, "periodic timer4, 100ms"), mdtimer::PERIODIC_TIMER);
    (void)timer4->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    timer4->stop();

    // periodic timer
    auto timer5 = timerMgr->createTimer(
        1000, std::bind(timeoutCallback1Args, "periodic timer5, 1000ms"), mdtimer::PERIODIC_TIMER);
    (void)timer5->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));

    (void)timer3->start();
    (void)timer4->start();
    (void)timer5->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    timer3->stop();
    timer4->stop();
    timer5->stop();

#if 0
    // periodic timer
    auto timer6 = timerMgr->createTimer(
        20, std::bind(timeoutCallback1Args, "periodic timer6, 20ms"), mdtimer::PERIODIC_TIMER);
    (void)timer6->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    timer6->stop();
#endif
#if 0
    // periodic timer
    auto timer7 = timerMgr->createTimer(
        10, std::bind(timeoutCallback1Args, "periodic timer7, 10ms"), mdtimer::PERIODIC_TIMER);
    for (auto i = 0; i < 2000; i++) {
        (void)timer7->start();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        timer7->stop();
        // std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
#endif

    LOGI("the end.");
    return 0;
}

运行log

2024-04-19 11:15:27.315719 13743 13743 I MDTimer:261 create timer id:1, timeout:50ms, type:0
2024-04-19 11:15:27.318055 13743 13743 I MDTimer:405 start timer id:1, timeout:50ms
2024-04-19 11:15:27.368745 13743 13745 I MDTimerTest:53 once timer0, 50ms function callback.
2024-04-19 11:15:27.418995 13743 13743 I MDTimer:424 stop timer id:1
2024-04-19 11:15:27.419178 13743 13743 I MDTimer:261 create timer id:2, timeout:50ms, type:0
2024-04-19 11:15:27.419338 13743 13743 I MDTimer:405 start timer id:2, timeout:50ms
2024-04-19 11:15:27.470682 13743 13745 I MDTimerTest:24 timeoutCallbackNoArgs.
2024-04-19 11:15:27.520261 13743 13743 I MDTimer:424 stop timer id:2
2024-04-19 11:15:27.520430 13743 13743 I MDTimer:405 start timer id:2, timeout:50ms
2024-04-19 11:15:27.531498 13743 13743 I MDTimer:424 stop timer id:2
2024-04-19 11:15:27.532010 13743 13743 I MDTimer:261 create timer id:3, timeout:50ms, type:0
2024-04-19 11:15:27.532442 13743 13743 I MDTimer:405 start timer id:3, timeout:50ms
2024-04-19 11:15:27.582829 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:once timer2, 50ms
2024-04-19 11:15:27.633664 13743 13743 I MDTimer:424 stop timer id:3
2024-04-19 11:15:27.633938 13743 13743 I MDTimer:261 create timer id:4, timeout:50ms, type:0
2024-04-19 11:15:27.634114 13743 13743 I MDTimer:405 start timer id:4, timeout:50ms
2024-04-19 11:15:27.689600 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:once timer3, 50ms
2024-04-19 11:15:27.735106 13743 13743 I MDTimer:424 stop timer id:4
2024-04-19 11:15:27.735266 13743 13743 I MDTimer:261 create timer id:5, timeout:100ms, type:1
2024-04-19 11:15:27.735442 13743 13743 I MDTimer:405 start timer id:5, timeout:100ms
2024-04-19 11:15:27.842323 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:27.935833 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.035738 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.136000 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.236246 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.335639 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.435584 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.536343 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.635616 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:28.735669 13743 13743 I MDTimer:424 stop timer id:5
2024-04-19 11:15:28.735952 13743 13743 I MDTimer:261 create timer id:6, timeout:1000ms, type:1
2024-04-19 11:15:28.736031 13743 13743 I MDTimer:405 start timer id:6, timeout:1000ms
2024-04-19 11:15:29.736116 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:30.736685 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:31.738570 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:32.737901 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:33.736643 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:33.761967 13743 13743 I MDTimer:405 start timer id:4, timeout:50ms
2024-04-19 11:15:33.762181 13743 13743 I MDTimer:405 start timer id:5, timeout:100ms
2024-04-19 11:15:33.762394 13743 13743 I MDTimer:405 start timer id:6, timeout:1000ms
2024-04-19 11:15:33.812689 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:once timer3, 50ms
2024-04-19 11:15:33.863057 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:33.963033 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.063345 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.163302 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.264890 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.384981 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.463853 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.572480 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.663067 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.763390 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.763459 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:34.901362 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:34.991213 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.064410 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.162356 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.263039 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.362304 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.462588 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.562535 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.662693 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.762447 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer4, 100ms
2024-04-19 11:15:35.762531 13743 13745 I MDTimerTest:29 timeoutCallback1Args info:periodic timer5, 1000ms
2024-04-19 11:15:35.779715 13743 13743 I MDTimer:424 stop timer id:4
2024-04-19 11:15:35.779879 13743 13743 I MDTimer:424 stop timer id:5
2024-04-19 11:15:35.780140 13743 13743 I MDTimer:424 stop timer id:6
2024-04-19 11:15:35.780478 13743 13743 I MDTimerTest:119 the end.
2024-04-19 11:15:35.780550 13743 13743 I MDTimer:269 destroy timer id:6
2024-04-19 11:15:35.780626 13743 13743 I MDTimer:424 stop timer id:6
2024-04-19 11:15:35.780702 13743 13743 I MDTimer:269 destroy timer id:5
2024-04-19 11:15:35.780776 13743 13743 I MDTimer:424 stop timer id:5
2024-04-19 11:15:35.780865 13743 13743 I MDTimer:269 destroy timer id:4
2024-04-19 11:15:35.780951 13743 13743 I MDTimer:424 stop timer id:4
2024-04-19 11:15:35.781061 13743 13743 I MDTimer:269 destroy timer id:3
2024-04-19 11:15:35.781146 13743 13743 I MDTimer:424 stop timer id:3
2024-04-19 11:15:35.781249 13743 13743 I MDTimer:269 destroy timer id:2
2024-04-19 11:15:35.781309 13743 13743 I MDTimer:424 stop timer id:2
2024-04-19 11:15:35.781382 13743 13743 I MDTimer:269 destroy timer id:1
2024-04-19 11:15:35.781451 13743 13743 I MDTimer:424 stop timer id:1
2024-04-19 11:15:35.781866 13743 13745 I MDTimer:398 TimerCallbackThread exit.
2024-04-19 11:15:35.782676 13743 13744 I MDTimer:367 TimerTickThread exit.

后续思考

  1. 本定时器设计简洁高效,应用方便,满足绝大多数场景的使用;
  2. 当需要创建或管理大量定时器时,由于定时器start时插入消息时间复杂度为O(log2N),但stop时直接删除消息时间复杂度为O(N),因此引入了辅助的map用于解决该问题,使时间复杂度变为log2N;也可以使用时间轮定时器来优化,但处理逻辑会比较复杂。
  3. 后续会推出基于时间轮的定时器设计。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux下使用C++实现定时器的方法有很多种。根据引用\[1\]中的描述,你可以自己编写一个定时器类来实现你的需求。下面是一个示例代码,你可以参考一下: ```cpp #include <iostream> #include <chrono> #include "timer.h" using namespace std; auto startTime = std::chrono::high_resolution_clock::now(); void timeout_callback(void){ auto endTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - startTime).count(); auto timeuse = endTime * 0.001; printf("Time elapsed %f ms\n", timeuse); exit(0); } int main() { timer t; t.timer_add(1, timeout_callback); startTime = std::chrono::high_resolution_clock::now(); getchar(); return 0; } ``` 在这个示例代码中,我们使用了`timer`类来实现定时器功能。你可以在`timer.h`文件中定义这个类,具体的实现可以参考引用\[3\]中的代码。在`main`函数中,我们创建了一个`timer`对象,并使用`timer_add`方法来添加一个定时任务,指定了定时的时间间隔和回调函数。当定时器触发时,会执行回调函数`timeout_callback`,并计算时间差。 希望这个示例代码能够帮助到你实现Linux下的C++定时器。如果你有其他问题,请随时提问。 #### 引用[.reference_title] - *1* *3* [Linux下C++定时器类Timer](https://blog.csdn.net/weixin_44001652/article/details/119025905)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [定时器-C++ Linux](https://blog.csdn.net/weixin_44881106/article/details/127240731)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值