17370845950

c++中如何实现简单计时器_c++ timer计时类封装实例【汇总】
标准C++无跨平台Timer类,推荐用std::thread+std::condition_variable实现可取消一次性定时器;周期性触发应基于主循环+std::chrono::steady_clock手动判断,避免阻塞和精度陷阱。

标准 C++ 没有内置的、跨平台的 Timer 类,所以“实现一个简单计时器”本质是封装系统级异步/轮询机制。直接用 std::this_thread::sleep_for + 循环是最易错的方式,不推荐用于真实计时逻辑。

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

这是最常用且可控的方案:启动一个分离线程,在指定延迟后通知回调,同时支持中途取消。关键不是“每隔多久执行”,而是“延迟多久后执行一次”。

常见错误现象:std::this_thread::sleep_for 在主线程里阻塞,导致 UI 冻结或无法响应中断;忘记加 std::mutex 保护取消标志,引发数据竞争。

  • 使用场景:网络超时、按钮防抖、延时初始化
  • 必须用 std::atomic 或带锁的 bool 记录 m_cancelled,否则多线程读写未同步会 UB
  • 不要在回调中直接操作 GUI 控件(如 Qt 的 QWidget),应发信号或投递到主线程
  • std::condition_variable::wait_for 比裸 sleep_for 更利于响应取消
class SimpleTimer {
    std::thread m_thread;
    std::atomic m_cancelled{false};
    std::function m_callback;

public:
    explicit SimpleTimer(std::function cb) : m_callback(std::move(cb)) {}

    void start(std::chrono::milliseconds delay) {
        if (m_thread.joinable()) m_thread.join();
        m_cancelled = false;
        m_thread = std::thread([this, delay] {
            std::mutex mtx;
            std::condition_variable cv;
            std::unique_lock lk(mtx);
            cv.wait_for(lk, delay, [this] { return m_cancelled.load(); });
            if (!m_cancelled.load()) m_callback();
        });
        m_thread.detach(); // 或者由使用者管理生命周期
    }

    void cancel() { m_cancelled = true; }
};

避免用 std::alarmsetitimer(Linux)做通用计时器

这些是信号驱动的底层接口,SIGALRM 不能安全调用大多数 C++ 对象(如 std::coutstd::vector::push_back),且信号处理函数中禁止 malloc/new、锁、IO 等操作。

使用场景仅限极简嵌入式或信号处理专用模块;现代 C++ 项目中基本被弃用。

  • std::signal(SIGALRM, handler) 注册的函数必须是 async-signal-safe 的,C++ 成员函数无法直接注册
  • 多个 SimpleTimer 实例共用同一个 SIGALRM,无法区分来源
  • Windows 下完全不可用,零跨平台性

需要周期性触发?用 std::chrono + 主循环手动判断(非阻塞)

GUI 应用、游戏主循环、嵌入式主干逻辑中,不应让计时器自己开线程,而应在每帧检查是否到期——这是最稳定、最易调试的方式。

性能影响:每次调用 std::chrono::steady_clock::now() 开销极小(通常几纳秒),远低于线程调度或系统调用。

  • 存储下次触发时间点(std::chrono::time_point),而非倒计时剩余毫秒数,避免累积误差
  • if (now >= next_fire) { callback(); next_fire += interval; },别用 while 防止卡顿导致连发
  • 不要把 std::this_thread::sleep_for 放进主循环来“省电”,它会让响应延迟不可控
class PeriodicTimer {
    std::chrono::steady_clock::time_point m_next_fire;
    std::chrono::milliseconds m_interval;
    std::func

tion m_callback; public: PeriodicTimer(std::chrono::milliseconds interval, std::function cb) : m_interval(interval), m_callback(std::move(cb)) { reset(); } void reset() { m_next_fire = std::chrono::steady_clock::now() + m_interval; } bool update() { // 返回 true 表示本次触发了回调 auto now = std::chrono::steady_clock::now(); if (now >= m_next_fire) { m_callback(); m_next_fire += m_interval; return true; } return false; } };

第三方库选型提醒:别为简单需求引入 heavy 依赖

Boost.Asio 的 boost::asio::steady_timer 功能完备,但强制绑定 io_context 和 strand,对单次延时任务属于杀鸡用牛刀;Qt 的 QTimer 依赖整个 Qt 框架,无法用于 headless 服务端。

真正容易被忽略的点:计时精度不等于系统时钟精度。std::chrono::steady_clock 在 Linux 上通常基于 CLOCK_MONOTONIC,但实际唤醒延迟受调度器影响,10ms 定时可能偏差 ±2ms —— 这不是 bug,是操作系统本质限制。硬实时场景必须用 RTOS 或内核模块。