文档目录

一、核心设计

定时器需要一个线程不断检查:“有哪些定时任务到期了?”

用 std::priority_queue(最小堆),堆顶是最快到期的任务。

struct TimerTask {
    std::chrono::steady_clock::time_point expiry;
    std::function<void()> callback;
    std::shared_ptr<bool> cancelled;  // 用于取消

    bool operator>(const TimerTask& other) const {
        return expiry > other.expiry;  // 小顶堆:最早到期的在堆顶
    }
};

class Timer {
    std::priority_queue<TimerTask, std::vector<TimerTask>, std::greater<>> tasks_;
    std::mutex mtx_;
    std::condition_variable cv_;
    bool stop_ = false;
    std::thread worker_;

public:
    Timer() {
        worker_ = std::thread([this] { worker_loop(); });
    }

    ~Timer() {
        {
            std::lock_guard<std::mutex> lock(mtx_);
            stop_ = true;
        }
        cv_.notify_one();
        worker_.join();
    }

    // 延迟 delay 后执行 callback
    // 返回的 TimerId 可用于取消
    std::shared_ptr<bool> addTimer(std::chrono::milliseconds delay,
                                    std::function<void()> callback) {
        auto cancelled = std::make_shared<bool>(false);
        TimerTask task{
            std::chrono::steady_clock::now() + delay,
            std::move(callback),
            cancelled
        };

        {
            std::lock_guard<std::mutex> lock(mtx_);
            tasks_.push(std::move(task));
        }
        cv_.notify_one();  // 唤醒定时线程,重新检查堆顶

        return cancelled;
    }

private:
    void worker_loop() {
        while (!stop_) {
            std::unique_lock<std::mutex> lock(mtx_);

            if (tasks_.empty()) {
                cv_.wait(lock, [this] { return stop_ || !tasks_.empty(); });
                continue;
            }

            auto now = std::chrono::steady_clock::now();
            auto& top = tasks_.top();

            if (now >= top.expiry) {
                // 到期
                auto task = std::move(tasks_.top());
                tasks_.pop();
                lock.unlock();  // 执行回调前解锁

                if (!*task.cancelled) {
                    task.callback();
                }
            } else {
                // 没到期,等待到最早到期的时刻
                cv_.wait_until(lock, top.expiry);
            }
        }
    }
};

二、关键点

主要数据结构:最小堆(priority_queue)
堆顶:最快到期的定时任务

线程循环:
   1. 检查堆顶是否到期 → 到期就执行并弹出
   2. 没到期就 wait_until 到到期时刻(精确定时)
   3. 有新任务加入时 notify 唤醒,重新检查

时间复杂度:

  • addTimer:$O(\log N)$(push 到堆)
  • 到期检查:$O(1)$(只查看堆顶)

三、周期定时器

std::shared_ptr<bool> addTimer(std::chrono::milliseconds delay,
                                std::chrono::milliseconds interval,
                                std::function<void()> callback) {
    auto cancelled = std::make_shared<bool>(false);

    // 用递归包装实现周期调用
    std::function<void()> periodic = [this, interval, callback, cancelled]() {
        if (*cancelled) return;
        callback();
        if (!*cancelled) {
            // 再次注册自己
            addTimer(interval, interval, callback);
        }
    };

    addTimer(delay, periodic);
    return cancelled;
}