Mac OS上的QThread :: msleep睡眠错误时间

时间:2018-06-01 09:58:51

标签: qt sleep qthread

我想使用qthread执行ping作业并在指定时间内休眠,但有时线程睡眠时间错误,我试过Qt5.6.3和5.9.3,两者都不起作用。

这是我的演示代码:

#include "mainwindow.h"
#include <QApplication>
#include <QThread>
#include <QDebug>
#include <QDateTime>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    QThread *thread = new QThread;
    QObject::connect(thread,&QThread::started,[=]{
        while(true){
            QDateTime dateTime = QDateTime::currentDateTime();
            qDebug() << dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") << "start..." ;
            QThread::msleep(7000);
        }
    });
    thread->start();

    return a.exec();
}

但是但是控制台输出是:

"2018-06-01 17:40:22.603" start...
"2018-06-01 17:40:29.608" start...
"2018-06-01 17:40:36.612" start...
"2018-06-01 17:40:43.613" start...
"2018-06-01 17:40:50.618" start...
"2018-06-01 17:40:57.623" start...
"2018-06-01 17:41:04.628" start...
"2018-06-01 17:41:11.629" start...
"2018-06-01 17:41:18.633" start...
"2018-06-01 17:41:25.634" start...
"2018-06-01 17:41:32.639" start...
"2018-06-01 17:41:39.640" start...
"2018-06-01 17:41:46.645" start...
"2018-06-01 17:41:53.650" start...
"2018-06-01 17:42:00.655" start...
"2018-06-01 17:42:07.659" start...
"2018-06-01 17:42:14.661" start...
"2018-06-01 17:42:21.666" start...
"2018-06-01 17:42:28.670" start...
"2018-06-01 17:42:35.674" start...
"2018-06-01 17:42:42.674" start...
"2018-06-01 17:42:59.673" start...

"2018-06-01 17:43:16.398" start...
"2018-06-01 17:43:23.399" start...
"2018-06-01 17:43:40.399" start...
"2018-06-01 17:43:50.297" start...
"2018-06-01 17:43:57.297" start...
"2018-06-01 17:44:14.297" start...
"2018-06-01 17:44:31.297" start...
"2018-06-01 17:44:48.296" start...
"2018-06-01 17:45:05.296" start...
"2018-06-01 17:45:22.296" start...
"2018-06-01 17:45:31.299" start...
"2018-06-01 17:45:48.299" start...
"2018-06-01 17:46:04.806" start...

我标记了错误行,有人知道为什么???? 感谢

2 个答案:

答案 0 :(得分:0)

QThread没有任何问题,也没有自我保护。它们根本不是为此任务而设计的。

macOS 不是一个实时操作系统,因此一旦你将一个线程置于睡眠状态,就绝对无法保证它会在一段精确的时间后唤醒。它将尽力而为,但无论出于何种原因,它可能需要更长/更短的时间。

如果您需要精确计时,可以使用 Qtimer Qt :: PreciseTimer (文档here)。

举个例子:

QTimer *timer = new QTimer(this);
timer->setTimerType(Qt::PreciseTimer)
timer->setInterval(7000);
connect(timer, &QTimer::timeout(), this, []{
   QDateTime dateTime = QDateTime::currentDateTime();
   qDebug() << dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") << "start..." ;
});
timer->start();

答案 1 :(得分:0)

这完全在您的方法的容差范围内。你没有运行某种实时控制系统。您正在桌面平台上运行它。这种计时准确性是预期的。

但你当然也做错了,因为这样的睡眠总会积累正时间漂移。当你在睡眠调用之间做更多的工作时,漂移会变得更糟:睡眠假设你从不睡过头,而I / O代码则需要零时间。这当然是一个错误的假设。系统计时器可以弥补这一点。

至少,确保您的睡眠补偿任何单据(mode = CompensatedSleep),或理想情况下使用计时器(mode = Timer)。下面的示例说明了所有三种方法,包括原始方法(mode = DriftySleep)。

// https://github.com/KubaO/stackoverflown/tree/master/questions/timer-modes-50640879
#include <QtWidgets>

struct Thread final : QThread {
   ~Thread() override { finish(); wait(); }
   void finish() { quit(); requestInterruption(); }
};

int main(int argc, char *argv[]) {
   QApplication app(argc, argv);
   QPushButton toggle{"Click to Stop"};
   toggle.setMinimumSize(300, 150);
   toggle.show();

   Thread thread;
   QTimer timer;
   QObject::connect(&toggle, &QPushButton::clicked, [&]{
      thread.finish();
      toggle.setDisabled(true);
   });
   QObject::connect(&thread, &Thread::finished, &toggle, &QWidget::close);

   constexpr enum { DriftySleep, CompensatedSleep, Timer } mode = CompensatedSleep;
   qint64 constexpr setPeriod = 7000;

   auto dump = [period = Q_INT64_C(0), watch = QElapsedTimer()](qint64 load = {}) mutable {
      qint64 current = watch.isValid() ? watch.elapsed() : 0;
      auto dateTime = QDateTime::currentDateTime();
      qDebug() << dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") << current;
      if (!watch.isValid()) {
         watch.start();
         period = load;
      } else
         period = watch.restart();
      return period;
   };
   if (mode != Timer) QObject::connect(&thread, &Thread::started, [&]{
      auto period = dump(setPeriod);
      while (!thread.isInterruptionRequested()) {
         QThread::msleep(setPeriod*2 - ((mode == CompensatedSleep) ? period : setPeriod));
         period = dump();
      }
   });
   else {
      timer.setTimerType(Qt::PreciseTimer);
      timer.start(setPeriod);
      timer.moveToThread(&thread);
      QObject::connect(&thread, &Thread::finished, [&]{ timer.moveToThread(thread.thread()); });
      QObject::connect(&thread, &Thread::started, [&]{ dump(setPeriod); });
      QObject::connect(&timer, &QTimer::timeout, [&]{ dump(); });
   }
   thread.start();
   return app.exec();
}