文章目录
- [QElapsedTimer 的使用详解](#QElapsedTimer 的使用详解)
-
- [1. 基本用法](#1. 基本用法)
- [2. 与 `QTime` 的区别](#2. 与
QTime的区别) - [3. 常见应用场景](#3. 常见应用场景)
-
- [3.1 性能测试](#3.1 性能测试)
- [3.2 节流 UI 更新](#3.2 节流 UI 更新)
- [3.3 超时检测](#3.3 超时检测)
- [3.4 帧率控制(游戏/动画)](#3.4 帧率控制(游戏/动画))
- [4. 与 `QTimer` 的区别](#4. 与
QTimer的区别) - [5. 注意事项](#5. 注意事项)
- 总结
- 最佳实践代码片段合集(速查表)
-
- [1. 性能分析](#1. 性能分析)
- [2. 节流 UI 更新(避免界面频繁刷新卡顿)](#2. 节流 UI 更新(避免界面频繁刷新卡顿))
- [3. 超时检测](#3. 超时检测)
- [4. 帧率控制(游戏/动画)](#4. 帧率控制(游戏/动画))
- [5. 周期性任务(简单版定时器)](#5. 周期性任务(简单版定时器))
QElapsedTimer 的使用详解
在 Qt 程序中,我们经常需要测量时间间隔,比如统计函数耗时、定时刷新 UI、实现节流/限流逻辑等。Qt 提供了一个轻量级类 QElapsedTimer ,它专门用于 高精度时间测量 。相比 QTimer(依赖事件循环、用于异步定时任务),QElapsedTimer 更适合在代码逻辑里测量时间间隔。
1. 基本用法
QElapsedTimer 的常见使用模式是:
cpp
QElapsedTimer timer;
timer.start(); // 记录当前时间
... // 执行一些耗时操作
qint64 ms = timer.elapsed();// 计算从 start() 到现在的毫秒数
核心方法有:
start():启动计时器,相当于记录当前时间戳。restart():返回上一次start()/restart()到现在的毫秒数,并重新启动计时器。elapsed():返回从start()/restart()到现在的毫秒数。isValid():判断计时器是否已经被启动。
示例:
cpp
QElapsedTimer timer;
timer.start();
doSomething();
qDebug() << "耗时:" << timer.elapsed() << "ms";
2. 与 QTime 的区别
Qt 里还有一个类 QTime 也能测量时间差,但 QElapsedTimer 更适合 高精度计时:
QElapsedTimer基于系统的单调时钟(monotonic clock),不会受系统时间修改影响。QTime则是基于系统时间,如果用户修改了系统时间,结果可能不正确。
因此,推荐在性能分析、逻辑节流等场景下使用 QElapsedTimer。
3. 常见应用场景
3.1 性能测试
cpp
QElapsedTimer timer;
timer.start();
heavyFunction();
qDebug() << "函数耗时:" << timer.elapsed() << "ms";
非常适合在调试时测量某段代码的执行效率。
3.2 节流 UI 更新
有些后台线程会频繁产生事件(比如数据回放、网络消息),直接更新 UI 会导致界面卡顿。这时候可以用 QElapsedTimer 控制 UI 更新频率。
cpp
void DataReplay::onFrameArrived(const std::string &f)
{
static QElapsedTimer timer;
int pos = slider_->tsToSec(QString::fromStdString(f));
if (!timer.isValid() || timer.elapsed() > 1000) { // 每秒更新一次
timer.restart();
QMetaObject::invokeMethod(
this,
[=] { slider_->setPosition(pos); },
Qt::QueuedConnection
);
}
}
这里的关键点:
- static 让
timer在多次回调中保持状态; elapsed()控制更新间隔(例如 1000 毫秒);Qt::QueuedConnection确保 UI 更新在主线程执行。
3.3 超时检测
例如:等待某个条件满足,如果超过时间就退出。
cpp
QElapsedTimer timer;
timer.start();
while (!ready()) {
if (timer.elapsed() > 5000) { // 超过 5 秒
qWarning() << "等待超时";
break;
}
QThread::msleep(100);
}
3.4 帧率控制(游戏/动画)
在循环中根据 elapsed() 控制帧率:
cpp
QElapsedTimer timer;
timer.start();
while (running) {
renderFrame();
int elapsed = timer.elapsed();
if (elapsed < 16) // ~60 FPS
QThread::msleep(16 - elapsed);
timer.restart();
}
4. 与 QTimer 的区别
QElapsedTimer:同步、阻塞式测量,用于计算耗时或控制循环。QTimer:异步事件驱动,用于定时触发槽函数。
举例:
- 想知道函数执行多久 → 用
QElapsedTimer; - 想每隔 1 秒刷新一次界面 → 用
QTimer。
5. 注意事项
-
不要忘记
restart()或start()如果直接调用
elapsed()而没有先start(),返回值可能没意义。 -
适合短时间测量
elapsed()返回qint64毫秒数,但并不是无限大的,通常可以保证数天范围内的精度,长时间测量建议用QDateTime。 -
线程安全
QElapsedTimer本身不是共享对象,每个线程应该有自己独立的实例。如果需要跨线程节流,可以考虑std::chrono或自己加锁。
总结
QElapsedTimer是 Qt 提供的 高精度时间测量工具。- 相比
QTime,它基于单调时钟,不受系统时间修改影响,更适合性能测试、逻辑节流、超时检测。 - 配合
restart()和elapsed(),能非常方便地实现节流 UI 更新、循环控制、帧率限制等功能。
一句话概括:
当你需要在 Qt 里测量"过去了多少毫秒"时,优先使用 QElapsedTimer。
最佳实践代码片段合集(速查表)
1. 性能分析
cpp
QElapsedTimer timer;
timer.start();
// 需要测量耗时的代码
heavyFunction();
qDebug() << "耗时:" << timer.elapsed() << " ms";
2. 节流 UI 更新(避免界面频繁刷新卡顿)
cpp
void DataReplay::onFrameArrived(const std::string &f)
{
static QElapsedTimer timer;
int pos = slider_->tsToSec(QString::fromStdString(f));
// 每 1000ms 最多更新一次
if (!timer.isValid() || timer.elapsed() > 1000) {
timer.restart();
QMetaObject::invokeMethod(
this,
[=] { slider_->setPosition(pos); },
Qt::QueuedConnection // 保证在 UI 线程执行
);
}
}
3. 超时检测
cpp
QElapsedTimer timer;
timer.start();
while (!ready()) {
if (timer.elapsed() > 5000) { // 超过 5 秒
qWarning() << "等待超时!";
break;
}
QThread::msleep(100);
}
4. 帧率控制(游戏/动画)
cpp
QElapsedTimer timer;
timer.start();
while (running) {
renderFrame();
int elapsed = timer.elapsed();
if (elapsed < 16) // 约等于 60 FPS
QThread::msleep(16 - elapsed);
timer.restart();
}
5. 周期性任务(简单版定时器)
cpp
QElapsedTimer timer;
timer.start();
while (running) {
if (timer.elapsed() > 2000) { // 每 2 秒执行一次
qDebug() << "定时任务";
timer.restart();
}
QThread::msleep(50);
}
📌 小结:
- 用
QElapsedTimer测 代码耗时 、控 更新频率 、做 超时检测 ,比QTimer更轻量、精确。 - 它不依赖事件循环,可以安全地用于后台线程。
- 和
QTimer互补:QTimer用来"准时触发任务",QElapsedTimer用来"计算经过了多久"。