QThread::msleep 在 MacOS 上休眠错误时间
QThread::msleep on MacOS sleep a error time
我想用qthread做一个ping job并在指定的时间休眠,但有时线程休眠时间是错误的,我在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...
我标记了错误行,有人知道为什么吗????
谢谢
QThread 和 msleep 本身都没有问题。它们根本不是为这项任务而设计的。
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();
这完全在您的方法的容差范围内。您不是 运行 某种实时控制系统。您 运行 它在桌面平台上。这样的计时精度是意料之中的。
但你当然也做错了,因为这样的睡眠总是会积累正的时间漂移。随着您在 sleep 调用之间做更多的工作,漂移会变得更糟:sleep 假设您从未睡过头,并且 I/O 代码花费零时间。这当然是一个错误的假设。系统计时器对此进行补偿。
至少,确保您的睡眠可以弥补任何失误 (mode = CompensatedSleep
),或者最好使用计时器 (mode = Timer
)。下面的示例说明了所有三种方法,包括您原来的方法 (mode = DriftySleep
)。
// https://github.com/KubaO/Whosebugn/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();
}
我想用qthread做一个ping job并在指定的时间休眠,但有时线程休眠时间是错误的,我在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...
我标记了错误行,有人知道为什么吗???? 谢谢
QThread 和 msleep 本身都没有问题。它们根本不是为这项任务而设计的。
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();
这完全在您的方法的容差范围内。您不是 运行 某种实时控制系统。您 运行 它在桌面平台上。这样的计时精度是意料之中的。
但你当然也做错了,因为这样的睡眠总是会积累正的时间漂移。随着您在 sleep 调用之间做更多的工作,漂移会变得更糟:sleep 假设您从未睡过头,并且 I/O 代码花费零时间。这当然是一个错误的假设。系统计时器对此进行补偿。
至少,确保您的睡眠可以弥补任何失误 (mode = CompensatedSleep
),或者最好使用计时器 (mode = Timer
)。下面的示例说明了所有三种方法,包括您原来的方法 (mode = DriftySleep
)。
// https://github.com/KubaO/Whosebugn/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();
}