QThread中的精确间隔

时间:2014-01-20 11:14:02

标签: c++ qt qthread

我在Qt写了一个很多东西(计算,数据采样......) 该线程必须以1000毫秒的间隔运行 计时器允许的错误大约是5毫秒 我已将线程的优先级更改为QThread::HighPriority,但线程在大约1060ms-1100ms的间隔内运行。
如何使间隔更精确? (我已将QThread子类化,并在msleep(interval)方法中使用run()

2 个答案:

答案 0 :(得分:8)

您已将线程的run()方法编码为:

void MyThread::run() {
  forever {
    doSomething();
    msleep(1000);
  }
}

有几个问题:

  1. doSomething()不会花费任何时间。至少,您需要计算doSomething()花费的时间和睡眠时间超过1000毫秒。

  2. doSomething() msleep()都可能花费不定的时间,因为您的线程永远不会被保留,也不会立即被保证一旦睡眠到期,它就可以开始运行。因此,您需要绝对跟踪时间,而不是相对于doSomething()的开头。

  3. 您正在使用通用睡眠功能,而无需利用底层平台可能提供的更好的API。

  4. 使用此伪代码表示一个合理正确的方法:

    const qint64 kInterval = 1000;
    qint64 mtime = QDateTime::currentMSecsSinceEpoch();
    forever {
      doSomething();
      mtime += kInterval;
      qint64 sleepFor = mtime - QDateTime::currentMSecsSinceEpoch();
      if (sleepFor < 0) {
        // We got preempted for too long - for all we know, the system could
        // have even gotten suspended (lid close on a laptop).
        // Note: We should avoid the implementation-defined behavior of 
        // modulus (%) for negative values.
        sleepFor = kInterval - ((-sleepFor) % kInterval);
      }
      OS_Precise_Wait_ms(sleepFor); // use the appropriate API on given platform
    }
    

    幸运的是,Qt提供了一个API,可以为您完成所有这些:定时器。它们是合理表现的周期性“滴答声”的来源。大多数天真的重新实现此功能可能会以某种方式弄错,因为它并不像它看起来那么简单。

    以下是重新组织代码的方法:

    class Worker : public QObject {
      QBasicTimer m_timer;
      void doSomething() {
        // do the work
      }
      void timerEvent(QTimerEvent * ev) {
        if (ev->timerId() != m_timer.timerId()) {
            QObject::timerEvent(ev);
            return;
        }
        doSomething();
      }
    public:
      Worker(QObject * parent = 0) : QObject(parent) {
        m_timer.start(1000, Qt::PreciseTimer, this);
      }
    };
    
    int main(int argc, char ** argv) {
      QCoreApplication app(argc, argv);
      Worker worker;
      QThread workerThread;
      worker.moveToThread(workerThread);
      workerThread.start(QThread::HighPriority);
    
      // Example of how to terminate the application after 10 seconds
      // Requires Qt 5 and a C++11 compiler.
      QTimer timer;
      QObject::connect(&timer, &QTimer::timeout, [&](){
        workerThread.quit();
        workerThread.wait();
        app.quit();
      });
      timer.setTimerType(Qt::VeryCoarseTimer);        
      timer.setSingleShot(true);
      timer.start(10000);
    
      return app.exec();
    }
    

答案 1 :(得分:1)

来自 QTimer 类的文档:

  

准确度和计时器分辨率

     

计时器的准确性取决于底层操作系统和   硬件。但是,大多数平台支持1毫秒的分辨率   定时器的精度在很多方面都不等于这个分辨率   现实世界的情况。

     

准确度还取决于计时器类型。对于 Qt :: PreciseTimer ,   QTimer将尝试将精度保持在1毫秒。精确的计时器   也永远不会超出预期的时间。

     

对于 Qt :: CoarseTimer Qt :: VeryCoarseTimer 类型,QTimer可能会唤醒   早于预期,在这些类型的利润率:5%的   Qt :: CoarseTimer的间隔和Qt :: VeryCoarseTimer的500 ms。

     

如果系统繁忙,所有计时器类型可能会超出预期的时间   或无法提供所要求的准确性。在这种超时的情况下   溢出,即使多次超时,Qt也只会发出一次activate()   已经过期,然后将恢复原来的间隔。