碰到个很神奇的问题,Qt计时器读数不准确

  • ACG

    需要统计一个精确到1ms的时间
    一开始我用的是QTimer类,间隔1ms让一个整形量自增1,结果发现QTimer效率还是太低,虽然可以精确到毫秒,但是没法准确控制间隔为1ms,具体来说平均间隔大约为1.2ms。
    不过后来我发现这个量没必要由我自己去维护,Qt内置了QElapsedTimer类,专门用来计时,可以精确到μs,使用elapsed()方法直接获取start()开始计时后经过的毫秒数。最终测试结果确实比较准确,平均间隔可以达到1.0ms。
    不过我录了一个120FPS的视频逐帧分析后发现,这个读数的尾数浮动其实非常大,理论上120FPS的视频每帧间隔是8.33ms,但是用elapsed()方法获取到的整形值的间隔却是4-11,浮动高达大约50%。理论上可以精确到μs的计时器不可能在ms数量级上产生这么大的误差啊...
    虽然有可能是GUI绘制耗时产生的误差,但是原来使用QTimer时也没遇到这么大的误差。


    显示当前经过毫秒的源代码如下

    #include "msecsgetter.h"
    #include "ui_msecsgetter.h"
    
    #include <QDateTime>
    
    //定义重绘GUI的间隔,单位ms
    #define INTERVAL 4
    
    MSecsGetter::MSecsGetter(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::MSecsGetter)
    {
        ui->setupUi(this);
    
        timer = new QTimer(this); //定时器,用来定时触发重绘GUI
        tick = new QElapsedTimer(); //计时器,用来记录经过的毫秒数
    
        //当计时器完成一次计时后,将GUI上的label内数字重绘为当前经过的毫秒数
        connect(timer, &QTimer::timeout,
                [=]()
        {
            ui->label->setText(QString::number(startTime + tick->elapsed()));
        });
    
        startTime = QDateTime::currentMSecsSinceEpoch(); //以自1970-01-01 00:00:00经过的毫秒数为初值
    
        //启动定时器和计时器
        tick->start();
        timer->start(INTERVAL);
    
    }
    
    MSecsGetter::~MSecsGetter()
    {
        delete ui;
    }
    

    后续:找到问题原因了,GUI重绘间隔的问题,把间隔调成1就好了,不过因为GUI重绘比较耗时(至少比让一个整形自增费时),所以会有约1ms的误差