我在同一个线程中有多个QTimer,在连接到QTimer的插槽中,我使用QEventLoop进行同步,就像http请求一样,但是我发现当它们以不同的顺序启动时,不同的QTimers可能会相互影响。
下面是我的简单测试代码片段:
案例1
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTimer t1;
QTimer t2;
QObject::connect(&t1, &QTimer::timeout, []()->void{
qDebug() << QDateTime::currentDateTimeUtc() << "T1...";
QEventLoop loop;
// t1 slot run time 3000ms
QTimer::singleShot(3000, &loop, SLOT(quit()));
loop.exec();
});
QObject::connect(&t2, &QTimer::timeout, []()->void{
qDebug() << QDateTime::currentDateTimeUtc() << "T2...";
QEventLoop loop;
// t2 slot run time 100ms
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();
});
// interval 1000ms, start t1 first and then t2
t1.start(1000);
t2.start(1000);
return a.exec();
}输出:
QDateTime(2022-03-21 14:00:51.014 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:00:51.016 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:00:54.025 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:00:54.027 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:00:58.018 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:00:58.019 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:01:01.014 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:01:01.015 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:01:04.016 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:01:04.016 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:01:07.019 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:01:07.020 UTC Qt::TimeSpec(UTC)) T1...从输出中可以看到,t2受t1影响,实际间隔约为3000~4000 is,对于t1和t2,我认为t2不应该受到影响。
案例2
只需更改QTimer启动顺序,但事情会变得不同
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTimer t1;
QTimer t2;
QObject::connect(&t1, &QTimer::timeout, []()->void{
qDebug() << QDateTime::currentDateTimeUtc() << "T1...";
QEventLoop loop;
// t1 slot run time 3000ms
QTimer::singleShot(3000, &loop, SLOT(quit()));
loop.exec();
});
QObject::connect(&t2, &QTimer::timeout, []()->void{
qDebug() << QDateTime::currentDateTimeUtc() << "T2...";
QEventLoop loop;
// t2 slot run time 100ms
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();
});
// interval 1000ms, start t2 first and then t1
t2.start(1000);
t1.start(1000);
return a.exec();
}输出:
QDateTime(2022-03-21 14:04:53.653 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:04:53.656 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:04:54.659 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:04:55.655 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:04:56.656 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:04:56.656 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:04:57.664 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:04:58.657 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:04:59.665 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:04:59.667 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:05:00.660 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:05:01.662 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:05:02.652 UTC Qt::TimeSpec(UTC)) T2...
QDateTime(2022-03-21 14:05:03.650 UTC Qt::TimeSpec(UTC)) T1...
QDateTime(2022-03-21 14:05:03.653 UTC Qt::TimeSpec(UTC)) T2...现在t1不再影响t2,t1的实际间隔约为3000~4000 is,t2约为1000 is。
我真的很困惑,为什么不同的开始顺序会有不同的结果。有人能帮我解释一下吗?
平台:QT5.5.0 MinGW 32位,Windows
IDE: Qt Creator 3.4.2
您可以直接在main()中运行我的测试代码并检查测试结果。
谢谢。
编辑1:
我使用QEventLoop来同步RESTFul API的http请求,如下所示:
// m_http is QNetworkAccessManager
QNetworkReply *reply = m_http->get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();在计时器插槽中,只有一个同步http请求来轮询数据。通常情况下,请求会很快完成,但如果出现错误,有时会花费超过1000 it。我的应用程序需要同时与多个服务器通信,每个连接都有一个计时器来执行轮询,所有连接实例都在同一个线程中,因此所有计时器共享相同的事件循环。我的测试是模拟请求的一种简单方法,使用QTimer::singleShot作为按请求计算的时间开销。在QT中是否有更好的方法来同步http请求?使用QEventLoop可能不是最好的。
发布于 2022-03-21 15:37:47
首先,检查您的QTimer::timerType():默认的是Qt::CoarseTimer,它有5%的精度。您可能需要Qt::PreciseTimer代替。
其次,主Qt事件循环将序列化所有事件。不管他们是什么。它是一个独特的管道(它很容易成为应用程序的瓶颈,BTW)。在处理某些事件(如鼠标事件)之前,它也有“打包”某些事件的坏习惯。
最后,一个QTimer::singleShot也是一个真正的定时器,您可以在一个插槽内启动一个--这基本上是在事件循环上下文中执行的,您在两个不同的 QEventLoop对象上手动调用事件循环处理.
老实说,你的计时器做奇怪的事情并不奇怪。你能准确地说出你需要用这些定时器做什么吗?因为您的QEventLoop技巧所做的唯一一件事是在请求期间阻塞/搅乱计时器,即T1为3000 ms,T2为100 ms,这显然会与您为每个请求的基本1000 ms周期相混淆.
通常,您将接收信号(finished(QNetworkReply*)表示QNetworkAccessManager,但将是readyRead()表示QIODevice)连接到您的插槽,并且使用sender()允许单个时隙同时处理各种对象。您不需要手动同步任何东西,而是让Qt为您处理--这是使用框架的好处之一。因此,“回覆接收”就是这样做的。
对于发送请求,这取决于你。您可以使用各种解决方案:
一旦请求,
QTimer发送它们,而不会同时敲碎机器和服务器。请求被推入缓冲区ASAP.moveToThread()才能使它正常工作。我更喜欢解决方案2,为每个目标服务器提供一个队列和一个特定的定时器--这将允许一个非常基本的QoS,并将避免对一个服务器的大量请求禁止使用其他服务器。在此之前,您可以使用dispatcher函数,该函数将通过映射自动选择要使用的正确队列,该映射关联服务器名称(如果需要的话是+协议)及其特定的队列和定时器。
当您什么都不做,请求(它们都被发送)并且等待回复时,您让主Qt事件循环完成它的工作:避免阻塞消息泵--它将产生“应用程序没有响应”消息。此外,它还允许减少CPU负载,获取应用程序的所有鼠标事件,并像进度条一样刷新GUI元素。
https://stackoverflow.com/questions/71559111
复制相似问题