QThread :: msleep на MacOS sleep время ошибки - PullRequest
0 голосов
/ 01 июня 2018

Я хочу использовать 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 голосов
/ 01 июня 2018

Это полностью в пределах допуска вашего подхода.Вы не используете какую-то систему управления в реальном времени.Вы запускаете его на настольной платформе.Ожидается такая точность синхронизации.

Но вы, конечно, тоже делаете это неправильно, так как такой сон всегда накапливает положительный сдвиг времени.Дрейф ухудшается, когда вы выполняете больше работы между вызовами в режиме сна: режим сна предполагает, что вы никогда не проспали, и что код ввода-вывода занимает нулевое время.Это, конечно, неверное предположение.Системные таймеры компенсируют это.

По крайней мере, убедитесь, что ваш сон компенсирует любые ошибки (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();
}
0 голосов
/ 01 июня 2018

Нет ничего плохого ни в QThread, ни в msleep.Они просто не предназначены для этой задачи.

macOS НЕ является операционной системой реального времени, поэтому после того, как вы уложите поток в спящий режим, нет абсолютно никакой гарантии, что он проснется через определенное время.Он сделает все возможное, но это может занять больше / меньше по любой причине.

Когда вам нужно точное время, вы можете использовать QTimer с Qt :: PreciseTimer (документация здесь ).

Как пример:

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();
...