QTimer 在所属线程的事件循环中安排后续工作。它适合定期刷新界面、轮询轻量状态和延迟执行一次操作。它不创建后台线程,也不保证每个回调都在指定时刻准时运行。
1. 一次 timeout 如何发生
跳转到“1. 一次 timeout 如何发生”sequenceDiagram participant Code as 应用代码 participant Timer as QTimer participant EventLoop as 线程事件循环 participant Slot as 槽或回调 Code->>Timer: 设置间隔并 start() Timer->>EventLoop: 注册计时请求 Note over EventLoop: 到期且事件循环可以处理事件 EventLoop->>Timer: 处理计时事件 Timer->>Slot: 发出 timeout() Slot-->>EventLoop: 尽快返回若线程还没有进入 exec(),或者被耗时循环阻塞,计时请求不能按预期得到处理。Qt 根据对象的线程归属处理定时器;启动和停止也必须在该定时器所属线程完成。依据:Qt 的定时器机制。
2. 可运行的周期任务
跳转到“2. 可运行的周期任务”下面是一个 Qt 6 Core 控制台程序,无需窗口或 Q_OBJECT 宏。它请求每 100 ms 输出一次,第三次输出后结束。
#include <QCoreApplication>#include <QDebug>#include <QElapsedTimer>#include <QTimer>
int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QTimer timer; QElapsedTimer elapsed; int count = 0;
timer.setInterval(100); timer.setTimerType(Qt::PreciseTimer); QObject::connect(&timer, &QTimer::timeout, &app, [&] { ++count; qInfo() << "tick" << count << "elapsed(ms)" << elapsed.elapsed(); if (count == 3) { timer.stop(); app.quit(); } });
elapsed.start(); timer.start(); return app.exec();}保存为 main.cpp,最小构建文件:
cmake_minimum_required(VERSION 3.21)project(TimerDemo LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)find_package(Qt6 REQUIRED COMPONENTS Core)add_executable(timer-demo main.cpp)target_link_libraries(timer-demo PRIVATE Qt6::Core)观察输出应有 tick 1、tick 2、tick 3。实际经过时间可能大于 100、200、300 ms,不能把这些数值当成严格验收阈值。这个例子用局部变量保存定时器,因为 app.exec() 返回之前作用域不会结束。
3. 单次执行与生命周期
跳转到“3. 单次执行与生命周期”在已经创建 app 的程序中安排一次回调:
QTimer::singleShot(250, &app, [] { qInfo() << "Run once after the delay";});带 context 的重载使回调与上下文对象关联:上下文销毁后不会再调用该回调。若 lambda 捕获其他裸指针,仍要另外保证它们的有效期。QTimer 文档说明了单次调用、线程与超时行为。
需要反复取消或重新安排时,保留一个 QTimer 对象,并设置 setSingleShot(true),用 start() 重置等待、用 stop() 取消。
QTimer debounce;debounce.setSingleShot(true);debounce.setInterval(300);QObject::connect(&debounce, &QTimer::timeout, &app, [] { qInfo() << "Perform a lightweight save request";});// 每次输入变化时调用 debounce.start();连续变化会重新开始等待。上例只是核心片段。实际界面中让 debounce 成为长期存活的成员,或者使用 new QTimer(this) 交给父对象管理,不能把它创建在立即返回的普通函数里再期待它继续触发。
4. 精度、延迟与计时用途
跳转到“4. 精度、延迟与计时用途”Qt::PreciseTimer 尝试提高精度并避免提前触发,系统忙时仍可晚到。发生多次到期积压时,Qt 不会机械补发同样数量的 timeout()。因此:
- 刷新界面可以用定时器,但计算真实经过时间应读取单调计时器,如
QElapsedTimer。 - 不要用“触发次数 × 设定间隔”作为准确时长。
- 采样时钟、运动控制脉冲等硬实时任务应由相应硬件或实时机制实现。
- 耗时 I/O 和计算应采用异步接口或工作线程;回调里的长任务同样会卡住事件循环。
QTimer 的整数间隔单位为毫秒,范围约 24 天。Qt 6.8 起还提供范围更大的 QChronoTimer;其更细的时间表示也不等于操作系统具有同样的实际调度精度。版本与范围参见 Qt Timers。
5. 排错顺序
跳转到“5. 排错顺序”timer.isActive()是否为真,间隔是否为合理的正数?- 对象是否仍存活,
connect是否连接到预期对象? - 所属线程是否运行事件循环,是否被阻塞?
- 是否从错误线程调用了
start()或stop()? - 是否把一次迟到理解成定时器停止,或者误把它当成独立后台线程?
原笔记只留下“定时器”标题。本页补足了可运行示例、生命周期和时序边界,作为后续 Qt 周期操作的统一入口。