用一个可被安全中断的等待循环,来模拟类似 QTimer 的“周期触发”。

#include <thread>
#include <atomic>
#include <chrono>
#include <functional>
#include <condition_variable>

class Timer
{
public:
    using Callback = std::function<void()>;

    Timer() = default;
    ~Timer() { stop(); }

    void start(int interval_ms, Callback cb)
    {
        stop();

        m_running = true;
        m_thread = std::thread([=]() {
            std::unique_lock<std::mutex> lock(m_mutex);
            while (m_running)
            {
                if (m_cv.wait_for(lock,
                        std::chrono::milliseconds(interval_ms),
                        [&]() { return !m_running; }))
                    break;

                cb(); // 定时触发
            }
        });
    }

    void stop()
    {
        m_running = false;
        m_cv.notify_all();
        if (m_thread.joinable())
            m_thread.join();
    }

private:
    std::thread m_thread;
    std::atomic<bool> m_running{false};
    std::condition_variable m_cv;
    std::mutex m_mutex;
};

一个常见的C++定时器实现(基于单独线程 + 条件变量的周期性定时器)。目标是低依赖(仅用 C++ 标准库)、精度较高(基于 steady_clock)、线程安全(无 busy-wait)且可随时安全中断的计时器。

不采用的方案

整体功能

每隔指定的毫秒数(interval_ms),在后台线程自动重复执行一次传入的回调函数cb(),直到调用stop()为止。

这里的回调用得很妙。因为回调的核心就是“你先把函数传给别人,别人在某个时机(未来)再反过来调用你的函数”

核心成员变量

成员 类型 作用
m_thread std::thread 负责定时循环的后台线程
m_running std::atomic<bool> 原子标志位,表示定时器是否应该继续运行(true=运行,false=停止)
m_cv std::condition_variable 条件变量,用来实现“等待指定时间”或“立即被唤醒停止”
m_mutex std::mutex 保护条件变量的互斥锁(条件变量必须搭配互斥锁使用)

关键函数

1. start(int interval_ms, Callback cb)

void start(int interval_ms, Callback cb)
{
    stop();  // 先停止旧的(如果有),保证“单线程单定时器”语义
    m_running = true;
    m_thread = std::thread([=]() { ... });
}

线程里执行的lambda内容(最核心部分):

std::unique_lock<std::mutex> lock(m_mutex);
while (m_running)
{
    if (m_cv.wait_for(lock,
            std::chrono::milliseconds(interval_ms),
            [&]() { return !m_running; }))
        break;
    cb(); // 定时触发
}

这段代码的作用是:

只要 m_running 还是 true,就一直循环做下面事情:
1. 等待 最多 interval_ms 毫秒
2. 在等待期间如果有人通过 m_cv.notify_all() 唤醒,并且此时 !m_running 了 → 立刻退出循环
3. 如果是正常超时(等满了 interval_ms) → 执行一次回调 cb()
4. 回到 1 继续下一轮

wait_for 的第三个参数(谓词)非常关键,它实现了“可被外部打断的睡眠”。

2. stop()

void stop()
{
    m_running = false;           // 1. 先把标志位置成停止
    m_cv.notify_all();           // 2. 立刻唤醒正在 wait_for 的线程
    if (m_thread.joinable())     // 3. 如果线程还活着
        m_thread.join();         //    等待它安全退出
}

这是最安全的停止方式三部曲:

调用方式

Timer t;

// 方式1:lambda(最推荐,灵活)
t.start(500, []{
    std::cout << "lambda 方式\n";
});

// 方式2:普通函数
void myTick() { std::cout << "普通函数\n"; }
t.start(600, myTick);

// 方式3:带参数的函数(用 std::bind)
void printNumber(int n) { std::cout << "数字: " << n << "\n"; }
t.start(700, std::bind(printNumber, 42));

// 方式4:类成员函数(推荐用 lambda 捕获 this)
class Worker {
public:
    void doWork() { std::cout << "工作...\n"; }
};
Worker w;
t.start(1200, [&w]{ w.doWork(); });

优缺点对比

优点:

有待优化:

❤️ 转载文章请注明出处,谢谢!❤️