我在Qt编写了一个线程,它做了很多事情(计算,数据采样......).
该线程必须以1000ms的间隔运行.
计时器允许的错误大约是5ms.
我已经改变了线程的优先级,QThread::HighPriority但是线程在大约1060ms-1100ms的间隔内运行.
如何使间隔更精确?(我已经将QThread子类化并msleep(interval)在run()方法中使用).
您已将线程的run()方法编码为:
void MyThread::run() {
forever {
doSomething();
msleep(1000);
}
}
Run Code Online (Sandbox Code Playgroud)
有几个问题:
doSomething()不花零时间.至少,你需要花费多长时间doSomething()和睡眠时间短于1000毫秒.
双方doSomething() 并 msleep()可以采取可变的时间量,因为你的线程永远不能保证被抢占,也不是保证立即开始,一旦它也会因为睡眠到期可运行的运行.因此,你需要绝对地跟踪时间,而不是相对于开始doSomething().
您正在使用通用睡眠功能,而无需利用底层平台可能提供的更好的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)