我在同一個執行緒中有多個QTimer,在連接到QTimer的槽中,我使用QEventLoop進行同步,就像http請求一樣,但是我發現不同的QTimer在以不同的順序啟動時可能會相互影響。
這是我的簡單測驗代碼片段:
情況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~4000ms左右,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~4000ms,t2 為 1000ms。
我真的很困惑為什么不同的開始順序會有不同的結果。誰能幫我解釋一下?
平臺:Qt 5.5.0 MinGW 32bit,Windows
IDE:Qt Creator 3.4.2
您可以直接在 main() 中運行我的測驗代碼并檢查測驗結果。
謝謝。
編輯1:
I am using QEventLoop for sync http request of RESTFul API, like this:
// m_http is QNetworkAccessManager
QNetworkReply *reply = m_http->get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
In timer slot, there is only a sync http request, to poll data periodly. Normally the request will be finished very fast, but sometimes it will cost more than 1000ms if error occurs. My application need to communicate with multiple server at the same time, and each connection have a timer to do the polling, all connection instances are in the same thread, so all timers share the same eventloop. My tests is a simple way to simulate the request, using QTimer::singleShot as the time cost by request. Is there any better way to do sync http request in QT? Using QEventLoop may be not the best.
uj5u.com熱心網友回復:
首先,檢查您的QTimer::timerType(): 默認為Qt::CoarseTimer,其精度為 5%。您可能需要Qt::PreciseTimer。
其次,主 Qt 事件回圈將序列化所有事件。不管他們是什么。這是一個獨特的管道(很容易成為您應用程式的瓶頸,順便說一句)。在處理它們之前將一些事件(如滑鼠事件)“打包”在一起也有一個壞習慣。
最后, a也是一個真正的計時器,你在一個槽內啟動一個 - 這基本上是在事件回圈背景關系中執行的,你在兩個不同的物件QTimer::singleShot上手動呼叫事件回圈處理...... QEventLoop
老實說,你的計時器做奇怪的事情并不奇怪。你能準確地說出你需要用這些計時器做什么嗎?因為您的技巧唯一要做的QEventLoop就是在請求的時間段內阻止/弄亂計時器,即 T1 為 3000 毫秒,T2 為 100 毫秒,這顯然會與您為每個請求的基本 1000 毫秒時間段混淆......
通常,您將接收信號(finished(QNetworkReply*)for QNetworkAccessManager,但它會是readyRead()for QIODevice)連接到您的插槽,并使用sender()以允許單個插槽同時處理各種物件。您無需手動同步任何內容,而是讓 Qt 為您處理 - 這是使用框架的好處之一。所以,“回復接收”就是這樣完成的。
對于發送請求,由您決定。您可以使用各種解決方案:
- 請求后盡快發送(即通過單擊按鈕)。
- 將它們緩沖到一個 FIFO 容器中,使用 a
QTimer發送它們而不會影響您的機器和服務器。請求被盡快推入緩沖區。 - 執行緒化這部分 - 請注意,使用 Qt,您需要大部分時間呼叫
moveToThread()才能使其正常作業。
我更喜歡解決方案#2,每個目標服務器都有一個佇列和一個特定的計時器——這將允許一個非常基本的 QoS,并將避免一個服務器的大量請求禁止使用其他服務器。在此之前,您可以使用調度程式函式,該函式將通過關聯服務器名稱(如果需要 協議)及其特定佇列和計時器的映射自動選擇要使用的正確佇列。
當你什么都不做時,既沒有請求(它們都被發送)并且你正在等待回復,你讓主 Qt 事件回圈完成它的作業:避免阻塞訊息泵 - 它會產生“應用程式是沒有回應”的訊息。此外,它還可以減少 CPU 負載、獲取應用程式的所有滑鼠事件以及重繪 進度條等 GUI 元素。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/450250.html
標籤:qt qtimer qeventloop
上一篇:Qt自定義繪制事件進度條
