一文搞懂C++高精度定时器实现原理

  1. 使用std::priority_queue容器自动在加入时排序
  2. 线程池可以动态变化
  3. 周期调度任务执行于线程池
  4. 定时时间到达任务排队到线程池执行

ThreadPool.h

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <atomic>
#include <memory>
#include <chrono>
#include <stdexcept>
#include <algorithm>
#include <list>
#include "LogLib.h"

namespace TaskScheduler
{
    const size_t CORE_THREADS = 2;
    const size_t MAX_THREADS = 8;
    const size_t MAX_QUEUE_SIZE = 8;
    const size_t THREAD_RECYCLE_TIME = 60;
    using taskJob = std::function<void()>;
    class ThreadPool
    {
    public:
        ThreadPool(size_t coreThreads = CORE_THREADS,
                   size_t maxThreads = MAX_THREADS,
                   size_t maxQueueSize = MAX_QUEUE_SIZE,
                   size_t idleTime = THREAD_RECYCLE_TIME)
            : m_coreThreads(std::max(CORE_THREADS, coreThreads)),
              m_maxThreads(std::max(MAX_THREADS, maxThreads)),
              m_maxQueueSize(std::max(MAX_QUEUE_SIZE, maxQueueSize)),
              m_idleTime(std::max(THREAD_RECYCLE_TIME, idleTime)),
              m_stop(false)
        {
            // 启动核心线程
            for (size_t i = 0; i < coreThreads; ++i)
            {
                AddThread();
            }
        }

        ~ThreadPool()
        {
            Shutdown(); // 在析构函数中调用 Shutdown 以确保线程池关闭。
        }

        // 提交任务,并返回 future 以获取结果
        template <class F, class... Args>
        auto Enqueue(F &&f, Args &&...args) -> std::future<decltype(f(args...))>
        {
            using return_type = decltype(f(args...));

            auto task = std::make_shared<std::packaged_task<return_type()>>(
                std::bind(std::forward<F>(f), std::forward<Args>(args)...));

            std::future<return_type> res = task->get_future();
            {
                std::unique_lock<std::mutex> lock(m_mtx);

                // 如果线程池已停止,则不再接受新任务
                if (m_stop.load())
                {
                    return res;
                }

                // 如果队列已满且线程未达最大值,则添加新线程
                if (m_tasks.size() >= m_maxQueueSize)
                {
                    if (m_threads.size() < m_maxThreads)
                    {
                        AddThread(); // 动态添加新线程
                    }
                    else
                    {
                        // 调用者运行策略
                        lock.unlock(); // 解锁避免死锁
                        (*task)();     // 在调用线程中执行任务
                        return res;
                    }
                }

                // 将任务添加到任务队列
                m_tasks.emplace([task]()
                                { (*task)(); });
            }
            m_cv.notify_one(); // 唤醒一个线程来处理任务
            return res;
        }

        // 关闭线程池,并等待所有线程完成
        void Shutdown()
        {
            {
                std::lock_guard<std::mutex> lock(m_mtx);
                m_stop.store(true); // 标志线程池停止接受新任务
            }
            m_cv.notify_all(); // 唤醒所有线程

            // 等待所有线程完成并退出
            for (auto &thread : m_threads)
            {
                if (thread.joinable())
                {
                    thread.join();
                }
            }
        }

    private:
        // 添加新线程到线程池
        void AddThread()
        {
            m_threads.emplace_back([this]
                                   {
                while (true) {
                    taskJob task;
                    {
                        std::unique_lock<std::mutex> lock(m_mtx);

                        // 等待任务或线程池关闭通知,支持空闲超时回收
                        if (!m_cv.wait_for(lock, m_idleTime,
                            [this] { return m_stop.load() || !m_tasks.empty(); })) {

                            // 保留核心线程
                            if (m_coreThreads >= m_threads.size()) continue;

                            // 如果线程空闲超过 idleTime,则回收该线程
                            lock.unlock(); // 解锁避免死锁
                            Enqueue ([this](std::thread::id self_id){
                                auto it = std::find_if(m_threads.begin(), m_threads.end(),
                                                       [self_id](const std::thread &t)
                                                       {
                                                           return t.get_id() == self_id;
                                                       });
                                if (it != m_threads.end())
                                {
                                    TRACE_LOG("Stopping and removing thread");
                                    if (it->joinable())
                                    {
                                        it->join();
                                    }
                                    m_threads.erase(it);
                                }
                            }, std::this_thread::get_id());
                            return;
                        }

                        if (m_stop.load() && m_tasks.empty()) {
                            return; // 如果线程池停止且任务为空,则退出
                        }

                        // 从任务队列中取出任务
                        task = std::move(m_tasks.front());
                        m_tasks.pop();
                    }
                    task(); // 执行任务
            } });
        }

        size_t m_coreThreads;            // 核心线程数
        size_t m_maxThreads;             // 最大线程数
        size_t m_maxQueueSize;           // 任务队列最大长度
        std::chrono::seconds m_idleTime; // 空闲线程的回收时间

        std::list<std::thread> m_threads; // 线程列表
        std::queue<taskJob> m_tasks;      // 任务队列

        std::mutex m_mtx;             // 互斥锁保护任务队列和线程列表
        std::condition_variable m_cv; // 条件变量用于线程同步
        std::atomic<bool> m_stop;     // 标志线程池是否停止
    };
}
#endif

TaskScheduler.h

#ifndef __INCTaskSchedulerh
#define __INCTaskSchedulerh

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>
#include <atomic>
#include <unordered_map>
#include <cstring>
#include "ThreadPool.h"

namespace TaskScheduler
{
    const int64_t TIMES_FOREVER = -1;
    using milliseconds = std::chrono::milliseconds;
    using qJob = std::function<void(void *)>;
    const int MAX_TASK_NAME_LEN = 32;
    // 定义任务结构
    typedef struct Task
    {
        char name[MAX_TASK_NAME_LEN];                      // 任务名称
        qJob taskFunc;                                     // 任务函数
        void *arg;                                         // 任务参数
        std::chrono::steady_clock::time_point executeTime; // 执行时间
        milliseconds interval;                             // 定时间隔(毫秒)(如果是周期任务,设置该值)
        bool periodic;                                     // 是否周期任务
        bool cancelled;                                    // 标记任务是否被取消
        int64_t execTimes;                                 // 执行次数

        Task(bool periodic) : periodic(periodic), cancelled(false)
        {
            ::memset(name, 0, MAX_TASK_NAME_LEN);
        }
    } TASK;
    using TASK_ID = std::shared_ptr<TASK>;

    // 用于任务的排序,按照执行时间排序
    struct TaskComparator
    {
        bool operator()(const TASK_ID a, const TASK_ID b) const
        {
            return a->executeTime < b->executeTime;
        }
    };
    class TaskScheduler
    {
    public:
        TaskScheduler();
        ~TaskScheduler();
        void Init();

        // 添加单次执行任务
        TASK_ID AddOneTimeTask(const char *name,
                               const qJob &taskFunc,
                               void *arg = nullptr,
                               milliseconds delay = std::chrono::seconds(5));

        // 添加周期性任务
        TASK_ID AddPeriodicTask(const char *name,
                                const qJob &taskFunc,
                                void *arg = nullptr,
                                milliseconds interval = std::chrono::seconds(5),
                                int64_t times = 1);

        // 终止任务
        bool RemoveTaskById(TASK_ID taskId);

    private:
        std::priority_queue<TASK_ID,
                            std::vector<TASK_ID>,
                            TaskComparator>
            m_taskQueue;                     // 优先队列,按执行时间排序任务
        std::mutex m_mtx;                    // 保护任务队列的互斥锁
        std::condition_variable m_cv;        // 条件变量,用于通知任务调度
        std::atomic<bool> m_stop;            // 控制调度器停止的标志
        std::shared_ptr<ThreadPool> m_pool;  // 线程池

        // 工作线程函数
        void Worker();
        TASK_ID PrepareTask(TASK_ID task, const char *name,
                            const qJob &taskFunc,
                            void *param,
                            milliseconds interval,
                            int64_t times);
    };
}
#endif

TaskScheduler.cpp

#include "TaskScheduler.h"

namespace TaskScheduler
{
    TaskScheduler::TaskScheduler() : m_stop(false)
    {
        m_pool = std::make_shared<ThreadPool>();
    }

    TaskScheduler::~TaskScheduler()
    {
        {
            std::lock_guard<std::mutex> lock(m_mtx);
            m_stop = true;
        }
        m_cv.notify_all();
    }

    void TaskScheduler::Init()
    {
        m_pool->Enqueue([this]()
                        {
                            this->Worker(); // 线程池异步执行调度任务
                        });
    }

    TASK_ID TaskScheduler::PrepareTask(TASK_ID task, const char *name,
                                       const qJob &taskFunc,
                                       void *param,
                                       milliseconds interval,
                                       int64_t times)
    {
        ::strncpy(task->name, name, MAX_TASK_NAME_LEN - 1);
        task->arg = param;
        task->taskFunc = taskFunc;
        task->executeTime = std::chrono::steady_clock::now() + interval;
        task->interval = interval;
        task->execTimes = times;
        {
            std::lock_guard<std::mutex> lock(m_mtx);
            m_taskQueue.push(task);
            m_cv.notify_one();
        }
        return task;
    }
    // 添加单次执行任务
    TASK_ID TaskScheduler::AddOneTimeTask(const char *name,
                                          const qJob &taskFunc,
                                          void *param,
                                          milliseconds delay)
    {
        return PrepareTask(std::make_shared<TASK>(false), name, taskFunc,
                           param, delay, 1);
    }

    // 添加周期性任务
    TASK_ID TaskScheduler::AddPeriodicTask(const char *name,
                                           const qJob &taskFunc,
                                           void *param,
                                           milliseconds interval,
                                           int64_t times)
    {
        return PrepareTask(std::make_shared<TASK>(true), name, taskFunc,
                           param, interval, times);
    }

    // 终止任务,根据任务ID将任务标记为已取消
    bool TaskScheduler::RemoveTaskById(TASK_ID taskId)
    {
        std::lock_guard<std::mutex> lock(m_mtx);
        m_cv.notify_one();
        return taskId->cancelled = true;
    }

    // 工作线程函数
    void TaskScheduler::Worker()
    {
        while (!m_stop)
        {
            std::unique_lock<std::mutex> lock(m_mtx);
            if (m_taskQueue.empty())
            {
                m_cv.wait(lock); // 如果任务队列为空,阻塞等待任务到来
            }
            else
            {
                auto now = std::chrono::steady_clock::now();
                auto nextTask = m_taskQueue.top(); // 获取最早的任务

                // 如果任务处于未取消且执行时间未到达
                if (!nextTask->cancelled && (now < nextTask->executeTime))
                {
                    // 等待直到任务的执行时间或有新任务到来
                    m_cv.wait_until(lock, nextTask->executeTime);
                    continue;
                }

                m_taskQueue.pop(); // 从队列中移除任务

                // 如果任务被取消,则跳过执行
                if (nextTask->cancelled)
                {
                    // 打印任务已取消的消息,并释放任务结构
                    TRACE_LOG("Task %s has been cancelled.\n", nextTask->name);
                    continue;
                }

                // 将任务提交给线程池异步执行
                m_pool->Enqueue([](TASK_ID pTask)
                                {
                                    // 打印任务名称
                                    TRACE_LOG("Task %s is running.\n", pTask->name);
                                    pTask->taskFunc(pTask->arg); // 执行任务
                                },
                                nextTask);

                // 如果是周期性任务,重新计算执行时间并放回队列
                if (nextTask->periodic &&
                    ((nextTask->execTimes == TIMES_FOREVER) ||
                     (--nextTask->execTimes != 0)))
                {
                    // 如果是周期性任务,重新计算执行时间并放回队列
                    nextTask->executeTime =
                        std::chrono::steady_clock::now() + nextTask->interval;
                    m_taskQueue.push(std::move(nextTask));
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值