Qt——線程與定時器

2021-12-27 Qt教程
一、定時器QTimer類

The QTimer class provides repetitive and single-shot timers.

The QTimer class provides a high-level programming interface for timers. To use it, create a QTimer, connect its timeout() signal to the appropriate slots, and call start(). From then on, it will emit the timeout() signal at constant intervals.

上面這段話摘自Qt助手文檔,我們使用QTimer類定義一個定時器,它可以不停重複,也可以只進行一次便停止。

使用起來也很簡單:

QTimer *timer = new QTimer(this);connect(timer, SIGNAL(timeout()), this, SLOT(update()));timer->start(1000);

創建一個QTimer對象,將信號timeout()與相應的槽函數相連,然後調用start()函數。接下來,每隔一段時間,定時器便會發出一次timeout()信號。

更多用法這裡就不講了,您可以自行參考官方文檔。比如如何停止、如何令定時器只運行一次等。

二、在多線程中使用QTimer1.錯誤用法

您可能會這麼做:

子類化QThread,在線程類中定義一個定時器,然後在run()方法中調用定時器的start()方法。

TestThread::TestThread(QObject *parent)    : QThread(parent){    m_pTimer = new QTimer(this);    connect(m_pTimer, &QTimer::timeout, this, &TestThread::timeoutSlot);} void TestThread::run(){    m_pTimer->start(1000);} void TestThread::timeoutSlot(){    qDebug() << QString::fromLocal8Bit("當前線程id:") << QThread::currentThread();}

接下來在主線程中創建該線程對象,並調用它的start()方法:

m_pThread = new TestThread(this);m_pThread->start();

看似十分自然,沒有什麼不妥,然而,編譯器將通知下面的錯誤信息:

 QObject::startTimer: Timers cannot be started from another thread

——定時器不能被其它線程start。

我們來分析一下:

剛開始只有主線程一個,TestThread的實例是在主線程中創建的,定時器在TestThread的構造函數中,所以也是在主線程中創建的。

當調用TestThread的start()方法時,這時有兩個線程。定時器的start()方法是在另一個線程中,也就是TestThread中調用的。

創建和調用並不是在同一線程中,所以出現了錯誤。

具體的原理可參考官方文檔——點我

每個QObject實例都有一個叫做「線程關係」(thread affinity)的屬性,或者說,它處於某個線程中。

默認情況下,QObject處於創建它的線程中。

當QObject接收隊列信號(queued signal)或者傳來的事件(posted event),槽函數或事件處理器將在對象所處的線程中執行。

根據以上的原理,Qt使用計時器的線程關係(thread affinity)來決定由哪個線程發出timeout()信號。正因如此,你必須在它所處的線程中start或stop該定時器,在其它線程中啟動定時器是不可能的。

 

2.正確用法一

在TestThread線程啟動後創建定時器。

void TestThread::run(){    m_pTimer = new QTimer();    m_pTimer->setInterval(1000);    connect(m_pTimer, &QTimer::timeout, this, &TestThread::timeoutSlot);    m_pTimer->start();    this->exec();}

有些地方需要注意:

1.不能像下面這樣給定時器指定父對象

m_pTimer = new QTimer(this);

否則會出現以下警告:

QObject: Cannot create children for a parent that is in a different thread.

(Parent is TestThread(0x709d88), parent's thread is QThread(0x6e8be8), current thread is TestThread(0x709d88) 

因為TestThread對象是在主線程中創建的,它的QObject子對象也必須在主線程中創建。所以不能指定父對象為TestThread。

2.必須要加上事件循環exec()

否則線程會立即結束,並發出finished()信號。

另外還有一點需要注意,與start一樣,定時器的stop也必須在TestThread線程中,否則會出錯。

void TestThread::timeoutSlot(){    m_pTimer->stop();    qDebug() << QString::fromLocal8Bit("當前線程id:") << QThread::currentThread();}

上面的代碼將出現以下錯誤:

QObject::killTimer: Timers cannot be stopped from another thread

綜上,子類化線程類的方法可行,但是不太好。 

 

3.正確用法二

無需子類化線程類,通過信號啟動定時器。

TestClass::TestClass(QWidget *parent)    : QWidget(parent){    m_pThread = new QThread(this);    m_pTimer = new QTimer();    m_pTimer->moveToThread(m_pThread);    m_pTimer->setInterval(1000);    connect(m_pThread, SIGNAL(started()), m_pTimer, SLOT(start()));    connect(m_pTimer, &QTimer::timeout, this, &ThreadTest::timeOutSlot, Qt::DirectConnection);}

通過moveToThread()方法改變定時器所處的線程,不要給定時器設置父類,否則該函數將不會生效。

在信號槽連接時,我們增加了一個參數——連接類型,先看看該參數可以有哪些值:

Qt::AutoConnection:默認值。如果接收者處於發出信號的線程中,則使用Qt::DirectConnection,否則使用Qt::QueuedConnection,連接類型由發出的信號決定。

Qt::DirectConnection:信號發出後立即調用槽函數,槽函數在發出信號的線程中執行。

Qt::QueuedConnection:當控制權返還給接收者信號的事件循環中時,開始調用槽函數。槽函數在接收者的線程中執行。

回到我們的例子,首先將定時器所處的線程改為新建的線程,然後連接信號槽,槽函數在定時器所處的線程中執行。

 

相關焦點

  • Python Qt GUI設計:QTimer計時器類、QThread多線程類和事件處理類(基礎篇—8)
    例如,如果需要執行一個特別耗時的操作,在執行過程中整個程序就會卡頓,效果就非常不理想或者Windows系統也認為程序運行出錯,自動關閉了程序。要解決這種問題就涉及多線程的知識。一般來說,多線程技術涉及三種方法,其中第一種是使用計時器模塊QTimer;第二種是使用多線程模塊QThread;最後是使用事件處理的功能。
  • 一份硬核的QT開發經驗及資料分享,長文收藏!
    https://wiki.qt.io/MainQt源碼查看網站?https://code.woboq.org/qt5Qt官方下載地址?https://download.qt.ioQt官方下載新地址?https://download.qt.io/new_archive/qt/Qt國內鏡像下載地址?https://mirrors.cloud.tencent.com/qtQt安裝包下載地址?
  • python3多線程實現一個語音報時,定時關機功能的應用
    下面是我的主函數:其中MainWindow1是pyqt生成的界面類,實例化給w.然後定義了一個1000ms的一個定時器,用於1s顯示一次時間(通過w.showtime方法完成的顯示)及報時和關機時間的判斷。關機時間也是1秒判斷一次的。其中多線程主要體現在語音報時功能上和關機時間的倒計時。
  • 乾貨| 小論定時器玩法(時間輪詢法)
    因此,樓主使用了一種類似線程管理的時間輪詢方式(可能用詞不當),來進行一個硬體定時器模擬多個軟體定時器(以下就說明為線程吧),支持線程註冊、註銷、掛起、喚醒、處理等接口。在使用上,只需要引用兩個接口,即可開始工作。
  • 從setTimeout/setInterval看JS線程
    我們先整理下js中定時器的相關知識,再來看這個問題。setInterval 缺點再次強調,定時器指定的時間間隔,表示的是何時將定時器的代碼添加到消息隊列,而不是何時執行代碼。所以真正何時執行代碼的時間是不能保證的,取決於何時被主線程的事件循環取到,並執行。
  • Qt多線程分享——線程局部存儲
    在多線程術語中,經常聽到一個詞就是「線程局部存儲」,英文Thread-local storage,簡稱TLS。我們今天就來看看這到底是一個什麼樣的神秘東西。1 在講解之前,我們先來看一個例子。這時就用到了線程局部存儲,在Qt中對應為QThreadStorage。你可以直接運行下面的示例,看一下輸出效果。
  • Qt 開發經驗總結
    定時器是個好東西,學會好使用它,有時候用QTimer::singleShot可以解決意想不到的問題。默認QtCreator是單線程編譯,可能設計之初考慮到儘量不過多佔用系統資源,而現在的電腦都是多核心的,默認msvc編譯器是多線程編譯的不需要手動設置,而對於其他編譯器,需要手動設置才行。
  • 一位大佬對於Qt學習的最全總結(三萬字乾貨)
    ,真正的耗時是在運算以及運算後的處理,而不是收發數據,在一些小數據量運算處理的項目中,一般不建議動用線程去處理,線程需要調度開銷的,不要什麼東西都往線程裡邊扔,線程不是萬能的。資料庫處理一般建議在主線程,如果非要在其他線程,務必記得打開資料庫也要在那個線程,即在那個線程使用資料庫就在那個線程打開,不能打開資料庫在主線程,執行sql在子線程,很可能出問題。
  • 一位大佬對於 Qt 學習的最全總結(三萬字乾貨)
    ,真正的耗時是在運算以及運算後的處理,而不是收發數據,在一些小數據量運算處理的項目中,一般不建議動用線程去處理,線程需要調度開銷的,不要什麼東西都往線程裡邊扔,線程不是萬能的。資料庫處理一般建議在主線程,如果非要在其他線程,務必記得打開資料庫也要在那個線程,即在那個線程使用資料庫就在那個線程打開,不能打開資料庫在主線程,執行sql在子線程,很可能出問題。
  • Qt線程之moveToThread
    上一篇我們通過子類化QThread的方式實現了多線程。這一次將使用另一種方式實現多線程,那就是通過moveToThread將對象移動到子線程(不知道這樣表達是否準確,反正意思差不多)。moveToThread是QObject的成員函數,那麼QObject又是誰?.....連大名鼎鼎的QObject都不知道,面壁一下先。
  • 一口氣, 了解 Qt 的所有 IPC 方式 | Qt 速學
    示例https://doc.qt.io/qt-5/qtnetwork-downloadmanager-example.html這個例子演示了如何使用 QNetworkAccessManager 實現一個命令行下載工具,類似 wget 命令。$ .
  • Linux C/C++定時器的實現原理和使用方法
    定時器的實現依賴的是CPU時鐘中斷,時鐘中斷的精度就決定定時器精度的極限。一個時鐘中斷源如何實現多個定時器呢?對於內核,簡單來說就是用特定的數據結構管理眾多的定時器,在時鐘中斷處理中判斷哪些定時器超時,然後執行超時處理動作。
  • 定時器有幾種實現方式?
    回到這篇文章的主題,我首先會圍繞第三個話題討論:設計實現一個定時器,可以使用什麼算法,採用什麼數據結構。接著再聊聊第一個話題:探討一些優秀的定時器實現方案。2 理解定時器很多場景會用到定時器,例如使用 TCP 長連接時,客戶端需要定時向服務端發送心跳請求。財務系統每個月的月末定時生成對帳單。
  • qt排序算法可視化
    函數名為 void MainWindow::initItems()在裡面寫著所以是這樣,用一個定時器來執行看起來像這樣那麼好,我們需要一個洗牌的操作,來個shuffle函數;這裡順便提及一下qt,一般都採用多線程進行處理,這樣可以保證主線程(界面)不會被佔用,導致用戶操作無效,出現等待無響應現象。
  • Qt信號槽原理
    (),原數據對象類型轉換,轉換成指定的類型,使用時一般傳入父類的名稱字符串;方法qt_metacall(),執行函數的回調,信號觸發;方法qt_static_metacall(),回調函數,被qt_metacall()調用,內部執行槽;這裡的幾個方法都沒有實現體,因為實現部分會有 moc 工具生成,在moc_xxx.cpp 文件中可以查看這些方法的實現體
  • 學習qt07QtCreator技巧
    qt擴展了元對象,信號與槽,屬性.在類中私處聲明Q_OBJECT宏,生成能夠支持元對象代碼的c++文件.metaObject返回元對象.其有許多接口,如QMetaObject::className()類名.newInstance新實例.inherits是否繼承某個串表示的類.tr和trUtf8可翻譯串,setProperty/property來取置屬性.
  • Qt值得學習嗎?詳解Qt的幾種開發方式
    qt值得學習嗎?嵌入式要學的東西真的很多,我們可能會說不寫界面的話就不用學qt了?我不贊同。Qt的實現主要是採用p-impl手法,實現接口與實現分離,它有很好的消息循環機制,有的對象與線程的相關性,它也有藉助moc生成反射元信息,這種設計方法至今仍然非常適用。qt內核部分其實是完全和界面無關的,你完全可以拋開Qt GUI部分來學習和使用Qt。
  • Qt-Thread線程官方的推薦的寫法
    序直接重寫QThread是沒有問題的,但是重寫QThread這種方式不太靈活,對於重寫的Thread來講,只有run函數才會在新的線程中
  • Qt框架QThread類線程基礎知識
    但是,具有多個線程的程序可以分配給多個內核,使事情以真正並發的方式發生。因此,將工作分發到多個線程可以使程序在多核 CPU 上運行得更快,因為可以使用其他內核。GUI 線程和輔助線程如前所述,每個程序在啟動時都有一個線程。此線程稱為"主線程"(在 Qt應用程式中也稱為"GUI 線程")。Qt GUI 必須在此線程中運行。