一、概述
QElapsedTimer类通常用于快速计算两个事件之间经过了多长时间。它的API与QTime类似,因此正在使用的代码可以快速移植到新类。通常我们计算的时间是用一个 QTime的两次减法,来计算差值,这个类也完全可以胜任。
但与QTime不同,QElapsedTimer试图在可能的情况下使用单调时钟。这意味着不能将QElapsedTimer对象转换为人类可读的时间。
该类的典型用例是确定在缓慢的操作中花费了多少时间。
二、使用
最简单的例子是用于调试,如下面的例子所示:
QElapsedTimer timer;
timer.start();
slowOperation1();
qDebug() << "The slow operation took" << timer.elapsed() << "milliseconds";
在这个例子中,定时器通过调用 start() 启动,运行时间由 elapsed() 函数计算。
经过的时间还可以用于在第一个操作完成后重新计算另一个操作的可用时间。当执行必须在一定时间内完成,但需要几个步骤时,这很有用。QIODevice及其子类中的 waitfor类型函数 就是这种需求的好例子。在这种情况下,代码可能如下所示:
void executeSlowOperations(int timeout)
{
QElapsedTimer timer;
timer.start();
slowOperation1();
int remainingTime = timeout - timer.elapsed();
if (remainingTime > 0)
slowOperation2(remainingTime);
}
另一个用例是在特定的时间片上执行特定的操作。为此,QElapsedTimer提供了 hasExpired() 函数,用于确定是否已经经过了一定的毫秒数:
void executeOperationsForTime(int ms)
{
QElapsedTimer timer;
timer.start();
while (!timer.hasExpired(ms))
slowOperation1();
}
在这种情况下,使用 QDeadlineTimer 通常更方便,它计算未来的超时时间,而不是跟踪经过的时间。
还有一个例子就是 使用 restart() 这个函数功能是,重启定时器并返回自上一次启动以来经过的时间。
这个函数等价于先用elapsed()获取经过的时间,然后用start()再次启动定时器,但它只需一次操作就完成了这一操作,无需两次获取时钟值。
下面的例子演示了如何使用这个函数校准一个慢操作(例如迭代计数)的参数,使该操作至少耗时250毫秒:
QElapsedTimer timer;
int count = 1;
timer.start();
do {
count *= 2;
slowOperation2(count);
} while (timer.restart() < 250);
return count;
三、参考时钟
QElapsedTimer将在所有支持它的平台中使用平台的参考时钟(参见QElapsedTimer::isMonotonic())。这有一个额外的好处,QElapsedTimer不受时间调整的影响,比如用户修正时间。另外,与QTime不同的是,QElapsedTimer不受时区设置的变化影响,例如夏令时。
另一方面,这意味着QElapsedTimer的值只能与使用相同引用的其他值进行比较。如果从QElapsedTimer对象(QElapsedTimer:: msecssinceference())中提取引用的时间并序列化,这些值永远不应该通过网络交换或保存到磁盘上,因为无法知道接收数据的计算机节点是否与发送数据的计算机节点相同,或者之后它是否重新启动了。
但可以与运行在同一机器上的其他进程交换该值,前提是它们也使用相同的参考时钟。QElapsedTimer将始终使用相同的时钟,因此可以安全地与来自同一机器中的另一个进程的值进行比较。如果与其他api产生的值进行比较,应该检查所使用的时钟是否与QElapsedTimer相同(参见QElapsedTimer::clockType())。
32位溢出
QElapsedTimer使用的一些时钟的范围有限,在达到上限(通常是32位)后可能溢出。QElapsedTimer处理这种溢出问题,并提供一致的计时。但是,当从QElapsedTimer提取自引用以来的时间时,同一机器中的两个不同进程可能对实际经过的时间有不同的理解。
关于哪些时钟类型可能溢出以及如何解决该问题的信息与时钟类型一起被记录下来。