MacOS下QThread::msleep睡眠时长异常问题求助
我看了你遇到的问题:在Qt 5.6.3和5.9.3中,用QThread执行循环任务,每次调用QThread::msleep(7000)期望休眠7秒,但偶尔会出现休眠时长远超预期的情况(比如你标记的17秒、9秒间隔)。先帮你拆解下可能的原因,再给你几个可行的解决办法。
你的测试代码
#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: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:40.399" start...
"2018-06-01 17:43:50.297" start...
可能的原因分析
系统时间被修改
你用QDateTime::currentDateTime()获取的是系统时间,如果运行过程中系统时间被NTP同步、手动调整,或者系统进入休眠后唤醒,都会导致时间跳变,看起来像是休眠时长不对,但实际msleep的休眠时长是正常的。这种情况在开启自动时间同步的机器上很常见。系统线程调度延迟
QThread::msleep()底层调用的是系统级休眠函数(比如Windows的Sleep()、Linux的nanosleep()),这些函数只能保证至少休眠指定时长。如果系统此时有高优先级任务占用CPU,操作系统会延迟唤醒你的线程,导致实际休眠时间变长。不过一般这种延迟不会超过几秒,像你遇到的17秒间隔,更可能是系统时间变动导致的。特定平台的Qt版本潜在问题
虽然Qt 5.6和5.9都是稳定版本,但在某些特定平台(比如旧版Windows)下,msleep可能存在精度问题,但这种情况概率较低。
解决办法
1. 先排查是否是系统时间问题
用QElapsedTimer代替系统时间来测量实际休眠时长,它基于系统的单调时钟,不受系统时间修改的影响。修改你的代码:
#include "mainwindow.h" #include <QApplication> #include <QThread> #include <QDebug> #include <QDateTime> #include <QElapsedTimer> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); QThread *thread = new QThread; QObject::connect(thread, &QThread::started, [=]{ QElapsedTimer timer; while(true){ QDateTime dateTime = QDateTime::currentDateTime(); qDebug() << dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") << "start..." ; timer.start(); QThread::msleep(7000); qDebug() << "实际休眠时长:" << timer.elapsed() << "ms"; } }); thread->start(); return a.exec(); }
如果输出的实际休眠时长接近7000ms,说明问题出在系统时间,和msleep无关;如果时长偏差很大,再考虑调度或Qt版本的问题。
2. 改用Qt事件循环+QTimer的方式(推荐)
Qt的线程设计更推荐事件驱动模型,而不是死循环+阻塞休眠。这种方式不仅避免了阻塞线程,定时精度也更可靠:
#include "mainwindow.h" #include <QApplication> #include <QThread> #include <QDebug> #include <QDateTime> #include <QTimer> class Worker : public QObject { Q_OBJECT public slots: void doPingTask() { QDateTime dateTime = QDateTime::currentDateTime(); qDebug() << dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") << "start..." ; // 在这里执行你的ping任务 } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); QThread *thread = new QThread; Worker *worker = new Worker; worker->moveToThread(thread); QTimer *timer = new QTimer; timer->setInterval(7000); // 7秒间隔 timer->moveToThread(thread); // 连接信号槽,启动线程后开始定时任务 QObject::connect(thread, &QThread::started, timer, QOverload<>::of(&QTimer::start)); QObject::connect(timer, &QTimer::timeout, worker, &Worker::doPingTask); // 清理资源 QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater); QObject::connect(thread, &QThread::finished, timer, &QTimer::deleteLater); QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); return a.exec(); } #include "main.moc"
这种方式利用Qt的事件循环处理定时,即使系统有调度压力,QTimer也会尽可能保证定时精度,同时线程可以处理其他事件,不会被阻塞。
3. 如果必须用死循环,调整休眠方式
如果一定要保留死循环,可以用QElapsedTimer来做补偿,确保每次循环的总间隔接近7秒:
while(true){ QDateTime dateTime = QDateTime::currentDateTime(); qDebug() << dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") << "start..." ; QElapsedTimer timer; timer.start(); // 执行你的ping任务 // ... // 计算剩余需要休眠的时间 qint64 elapsed = timer.elapsed(); if(elapsed < 7000){ QThread::msleep(7000 - elapsed); } }
这样可以抵消任务执行时间的影响,保证每次循环的总间隔接近7秒。
内容的提问来源于stack exchange,提问作者王堃澎

