一、核心设计
定时器需要一个线程不断检查:“有哪些定时任务到期了?”
用 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;
}