导读:本期聚焦于小伙伴创作的《C++如何实现带优先级的时间轮定时任务心跳检测调度优化方案》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++如何实现带优先级的时间轮定时任务心跳检测调度优化方案》有用,将其分享出去将是对创作者最好的鼓励。

在C++开发的服务端系统中,定时任务调度和心跳检测是支撑业务稳定运行的核心模块。传统基于排序链表或者最小堆的定时调度方案,在处理大量高频定时任务时,会出现插入、删除操作时间复杂度高的问题,而时间轮结构可以将任务触发的时间复杂度降低到O(1),结合优先级队列处理同时间槽内的任务优先级排序,能够很好地满足高并发场景下的调度需求。

C++如何实现带优先级的时间轮定时任务心跳检测调度优化方案

核心设计思路

整个方案的核心由三个部分组成:时间轮结构、带优先级的定时任务结构、心跳检测调度管理器。时间轮采用分层设计,分为秒级、分级、小时级三层,减少单个时间槽的任务数量;每个定时任务包含触发时间戳、优先级、回调函数三个核心属性;调度管理器负责时间轮的推进和任务的触发执行。

任务结构设计

定时任务需要包含优先级信息,优先级数值越小代表优先级越高,同时需要记录任务的唯一标识和触发时间,方便后续的任务取消操作。

#include <functional>
#include <chrono>

// 定时任务优先级枚举,数值越小优先级越高
enum class TaskPriority {
    HIGH = 0,
    NORMAL = 1,
    LOW = 2
};

// 定时任务结构
struct TimerTask {
    uint64_t task_id;               // 任务唯一ID
    int64_t trigger_time;           // 触发时间戳(毫秒)
    TaskPriority priority;          // 任务优先级
    std::function<void()> callback; // 任务回调函数

    // 优先级比较函数,用于优先级队列排序
    bool operator<(const TimerTask& other) const {
        // 先按触发时间排序,触发时间相同则按优先级排序
        if (trigger_time != other.trigger_time) {
            return trigger_time > other.trigger_time;
        }
        return static_cast<int>(priority) > static_cast<int>(other.priority);
    }
};

时间轮结构设计

采用三层时间轮设计,秒轮有60个槽,分轮有60个槽,小时轮有24个槽,每个槽对应一个优先级队列,存储该时间槽需要触发的任务。

#include <queue>
#include <vector>
#include <unordered_map>

// 时间轮三层槽数量定义
const int SECOND_SLOT_NUM = 60;
const int MINUTE_SLOT_NUM = 60;
const int HOUR_SLOT_NUM = 24;

// 时间轮结构
class TimeWheel {
private:
    // 三层时间轮的槽,每个槽是优先级队列
    std::vector<std::priority_queue<TimerTask>> second_slots;
    std::vector<std::priority_queue<TimerTask>> minute_slots;
    std::vector<std::priority_queue<TimerTask>> hour_slots;

    int current_second;
    int current_minute;
    int current_hour;

public:
    TimeWheel() 
        : second_slots(SECOND_SLOT_NUM),
          minute_slots(MINUTE_SLOT_NUM),
          hour_slots(HOUR_SLOT_NUM),
          current_second(0),
          current_minute(0),
          current_hour(0) {}

    // 添加任务到时间轮
    void add_task(const TimerTask& task) {
        int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        int64_t delay = task.trigger_time - now;
        if (delay < 0) {
            // 任务已经过期,直接执行
            task.callback();
            return;
        }

        // 计算延迟的时分秒
        int delay_sec = delay / 1000;
        int delay_hour = delay_sec / 3600;
        int delay_minute = (delay_sec % 3600) / 60;
        int delay_second = delay_sec % 60;

        if (delay_hour >= HOUR_SLOT_NUM) {
            // 超过时间轮最大范围,放入小时轮最后一个槽
            hour_slots[HOUR_SLOT_NUM - 1].push(task);
        } else if (delay_hour > 0) {
            int slot = (current_hour + delay_hour) % HOUR_SLOT_NUM;
            hour_slots[slot].push(task);
        } else if (delay_minute > 0) {
            int slot = (current_minute + delay_minute) % MINUTE_SLOT_NUM;
            minute_slots[slot].push(task);
        } else {
            int slot = (current_second + delay_second) % SECOND_SLOT_NUM;
            second_slots[slot].push(task);
        }
    }

    // 推进时间轮,每秒调用一次
    void tick() {
        // 处理当前秒槽的任务
        auto& sec_queue = second_slots[current_second];
        while (!sec_queue.empty()) {
            TimerTask task = sec_queue.top();
            sec_queue.pop();
            int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::system_clock::now().time_since_epoch()
            ).count();
            if (task.trigger_time <= now) {
                task.callback();
            } else {
                // 任务还没到触发时间,重新添加回时间轮
                add_task(task);
            }
        }

        current_second = (current_second + 1) % SECOND_SLOT_NUM;
        if (current_second == 0) {
            // 秒轮转完一圈,推进分轮
            current_minute = (current_minute + 1) % MINUTE_SLOT_NUM;
            // 将分轮当前槽的任务转移到秒轮
            auto& min_queue = minute_slots[current_minute];
            while (!min_queue.empty()) {
                TimerTask task = min_queue.top();
                min_queue.pop();
                add_task(task);
            }

            if (current_minute == 0) {
                // 分轮转完一圈,推进小时轮
                current_hour = (current_hour + 1) % HOUR_SLOT_NUM;
                // 将小时轮当前槽的任务转移到分轮
                auto& hour_queue = hour_slots[current_hour];
                while (!hour_queue.empty()) {
                    TimerTask task = hour_queue.top();
                    hour_queue.pop();
                    add_task(task);
                }
            }
        }
    }
};

心跳检测调度实现

心跳检测通常需要定期向客户端发送心跳包,同时检测客户端的心跳响应是否超时。我们可以把心跳发送任务作为定时任务放入时间轮,同时维护一个客户端心跳状态表,记录每个客户端最后一次收到心跳的时间。

#include <unordered_map>
#include <thread>
#include <atomic>
#include <iostream>

// 客户端心跳状态
struct ClientHeartbeatState {
    uint64_t client_id;
    int64_t last_heartbeat_time; // 最后一次收到心跳的时间(毫秒)
    int heartbeat_interval;      // 心跳发送间隔(毫秒)
};

// 心跳检测调度管理器
class HeartbeatScheduler {
private:
    TimeWheel time_wheel;
    std::unordered_map<uint64_t, ClientHeartbeatState> client_states;
    std::atomic<uint64_t> task_id_generator;
    std::unordered_map<uint64_t, uint64_t> task_client_map; // 任务ID到客户端ID的映射
    std::thread tick_thread;
    std::atomic<bool> running;

    // 发送心跳包的回调
    void send_heartbeat(uint64_t client_id) {
        std::cout << "Send heartbeat to client: " << client_id << std::endl;
        // 实际场景中这里会调用网络发送接口
        // 发送完成后,重新添加下一个心跳任务
        int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        if (client_states.count(client_id)) {
            int64_t next_trigger = now + client_states[client_id].heartbeat_interval;
            TimerTask task;
            task.task_id = task_id_generator++;
            task.trigger_time = next_trigger;
            task.priority = TaskPriority::NORMAL;
            task.callback = [this, client_id]() { send_heartbeat(client_id); };
            time_wheel.add_task(task);
            task_client_map[task.task_id] = client_id;
        }
    }

    // 时间轮推进线程函数
    void tick_loop() {
        while (running) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            time_wheel.tick();
            // 检查心跳超时的客户端
            check_heartbeat_timeout();
        }
    }

    // 检查心跳超时的客户端
    void check_heartbeat_timeout() {
        int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        for (auto it = client_states.begin(); it != client_states.end(); ) {
            // 超过3个心跳周期没收到响应则判定超时
            if (now - it->second.last_heartbeat_time > it->second.heartbeat_interval * 3) {
                std::cout << "Client timeout: " << it->second.client_id << std::endl;
                // 移除该客户端的所有任务
                remove_client_tasks(it->second.client_id);
                it = client_states.erase(it);
            } else {
                ++it;
            }
        }
    }

    // 移除客户端相关的所有任务
    void remove_client_tasks(uint64_t client_id) {
        // 实际场景中需要维护任务列表,这里简化逻辑
        // 可以在TimerTask中添加client_id字段,时间轮推进时过滤已移除的客户端任务
    }

public:
    HeartbeatScheduler() : task_id_generator(0), running(false) {}

    // 启动调度器
    void start() {
        running = true;
        tick_thread = std::thread(&HeartbeatScheduler::tick_loop, this);
    }

    // 停止调度器
    void stop() {
        running = false;
        if (tick_thread.joinable()) {
            tick_thread.join();
        }
    }

    // 添加客户端心跳检测
    void add_client(uint64_t client_id, int heartbeat_interval) {
        int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        client_states[client_id] = {client_id, now, heartbeat_interval};
        // 添加第一个心跳任务
        TimerTask task;
        task.task_id = task_id_generator++;
        task.trigger_time = now + heartbeat_interval;
        task.priority = TaskPriority::NORMAL;
        task.callback = [this, client_id]() { send_heartbeat(client_id); };
        time_wheel.add_task(task);
        task_client_map[task.task_id] = client_id;
    }

    // 更新客户端心跳时间
    void update_client_heartbeat(uint64_t client_id) {
        if (client_states.count(client_id)) {
            int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::system_clock::now().time_since_epoch()
            ).count();
            client_states[client_id].last_heartbeat_time = now;
        }
    }
};

优化调优方案

在实际使用中,可以根据场景需求对方案进行进一步优化:

  • 任务去重优化:如果同一个客户端重复添加心跳任务,可以先取消之前的任务再添加新的,避免重复发送心跳,减少无效任务占用时间槽资源。
  • 时间轮槽位动态调整:如果业务场景的定时任务集中在某个时间范围,可以动态调整对应层的槽位数量,减少内存占用。
  • 任务执行线程池化:时间轮触发任务后,将任务放入线程池执行,避免任务执行时间过长阻塞时间轮的推进,提升调度吞吐量。
  • 优先级队列优化:如果同时间槽内的任务数量非常多,可以使用更高效的优先级队列实现,比如斐波那契堆,降低插入和删除的时间复杂度。

使用示例

以下是调度器的简单使用示例,模拟两个客户端的心跳检测场景:

int main() {
    HeartbeatScheduler scheduler;
    scheduler.start();

    // 添加两个客户端,心跳间隔分别为1秒和2秒
    scheduler.add_client(1001, 1000);
    scheduler.add_client(1002, 2000);

    // 模拟客户端1在3秒后上报一次心跳
    std::this_thread::sleep_for(std::chrono::seconds(3));
    scheduler.update_client_heartbeat(1001);

    // 运行10秒后停止
    std::this_thread::sleep_for(std::chrono::seconds(10));
    scheduler.stop();
    return 0;
}

C++时间轮定时任务调度心跳检测优先级队列修改时间:2026-07-19 04:39:57

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。