QThread中的精确间隔

Mos*_*osi 3 c++ qt qthread

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

Rei*_*ica 8

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

void MyThread::run() {
  forever {
    doSomething();
    msleep(1000);
  }
}
Run Code Online (Sandbox Code Playgroud)

有几个问题:

  1. doSomething()不花零时间.至少,你需要花费多长时间doSomething()和睡眠时间短于1000毫秒.

  2. 双方doSomething() msleep()可以采取可变的时间量,因为你的线程永远不能保证被抢占,也不是保证立即开始,一旦它也会因为睡眠到期可运行的运行.因此,你需要绝对地跟踪时间,而不是相对于开始doSomething().

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

使用此伪代码表达一种合理正确的方法:

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
}
Run Code Online (Sandbox Code Playgroud)

幸运的是,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();
}
Run Code Online (Sandbox Code Playgroud)