C++怎么实现定时器_C++时间控制教程【触发】

1次阅读

std::this_Thread::sleep_for 不能当定时器用,因其仅阻塞当前线程、无回调、不可取消、不自动重复,仅适用于单次延时;伪定时循环会导致卡死、响应失效和精度偏差。

C++怎么实现定时器_C++时间控制教程【触发】

std::this_thread::sleep_for 为什么不能当定时器用

它只是让当前线程暂停,不触发回调、不自动重复、无法取消,本质是“延时”,不是“定时器”。真要轮询或等待某个时间点,它能凑合;但想在后台隔几秒执行一次 log_status() 或超时后发个通知,它直接失效。

常见错误现象:std::this_thread::sleep_for 套在 while 循环里做“伪定时”,结果主线程卡死、无法响应退出信号、精度漂移严重(尤其在 windows 上实际休眠比指定时间长)。

  • 只适合单次阻塞等待,比如初始化后等 100ms 再继续
  • 不要用它模拟周期任务——CPU 空转 + sleep 组合既耗电又不准
  • 如果必须用,优先搭配 std::chrono::steady_clock,别用 system_clock(可能被系统时间调整拖垮)

std::thread + std::condition_variable 实现可取消的单次定时器

这是 c++11 起最轻量、无外部依赖的靠谱方案,核心是让一个独立线程等待条件变量超时,到点后通知主逻辑,同时支持中途调用 cancel() 提前唤醒。

使用场景:http 请求超时控制、资源锁自动释放、GUI 操作倒计时反馈。

立即学习C++免费学习笔记(深入)”;

关键参数差异:std::condition_variable::wait_until 接收的是绝对时间点(std::chrono::time_point),不是间隔;而 wait_for 是相对时长,容易因系统调度偏差累积误差。

示例片段:

class SimpleTimer {     std::thread t_;     std::mutex mtx_;     std::condition_variable cv_;     bool cancelled_ = false;  public:     template<typename Rep, typename Period, typename F, typename... Args>     SimpleTimer(std::chrono::duration<Rep, Period> delay, F&& f, Args&&... args) {         t_ = std::thread([this, delay, f = std::forward<F>(f),                            args = std::make_tuple(std::forward<Args>(args)...)]() mutable {             auto tp = std::chrono::steady_clock::now() + delay;             std::unique_lock<std::mutex> lk(mtx_);             if (cv_.wait_until(lk, tp, [this]{ return cancelled_; })) {                 return; // 被 cancel             }             std::apply(f, args);         });     }      void cancel() {         {             std::lock_guard<std::mutex> lk(mtx_);             cancelled_ = true;         }         cv_.notify_all();     }      ~SimpleTimer() {         if (t_.joinable()) t_.join();     } };

Windows 上用 SetTimer / CreateWaitableTimer 容易踩的坑

这两个 API 表面简单,但和 C++ 对象生命周期一结合就出事。典型问题是:定时器回调函数里访问已析构的对象成员,或者 SetTimer 返回的 UINT_PTR 被当成裸指针传进回调,结果对象早没了。

性能影响:Windows GUI 线程的 SetTimer 会向消息队列投递 WM_TIMER,若主线程忙于计算,消息积压,定时器就“跳帧”;而 CreateWaitableTimer 配合 WaitForSingleObjectEx 更准,但必须手动管理等待线程。

  • 绝不要在回调里直接调用 this->do_something() —— 改用静态函数 + SetWindowLongPtr 存句柄,或用 std::shared_ptr 持有对象并传入 Lambda 捕获
  • CreateWaitableTimerbManualReset 设为 false 才能自动重置,否则第二次 WaitForSingleObject 会立刻返回
  • 跨 DLL 边界时,确保回调函数是 __stdcall,否则被破坏

linux 下 timerfd_create 怎么和 epoll 配合

这是 Linux 原生、高效、可嵌入事件循环的方案,timerfd_create 返回一个文件描述符,到期时 epoll_wait 就能感知到可读事件,不用额外线程。

兼容性注意:glibc 2.8+、内核 2.6.25+ 才支持;macos 和 Windows 完全不支持,别写死了。

容易漏的点:timerfd_settimeit_value 设为 0 表示“只设置间隔,不立即触发”,但很多人误设成非零导致第一次触发提前;另外,每次 read timerfd 必须读 8 字节(uint64_t),否则下次 epoll_wait 不再通知。

  • CLOCK_MONOTONIC,别用 CLOCK_REALTIME(NTP 调整会让定时器乱跳)
  • Struct itimerspec 的 it_interval 设为 0 表示只触发一次
  • 务必检查 read() 返回值,少于 8 字节说明出错或被中断

C++ 里没有“开箱即用”的跨平台定时器抽象,每个方案都得自己兜底处理取消、销毁、线程安全。最麻烦的不是写代码,而是厘清“谁负责 stop、谁负责 join、谁负责清理资源”——这些边界一旦模糊,use-after-freethread leak 就跟着来了。

text=ZqhQzanResources