Qt實(shí)現(xiàn)線程與定時(shí)器的方法
一、定時(shí)器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類定義一個(gè)定時(shí)器,它可以不停重復(fù),也可以只進(jìn)行一次便停止。
使用起來(lái)也很簡(jiǎn)單:
QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(1000);
創(chuàng)建一個(gè)QTimer對(duì)象,將信號(hào)timeout()與相應(yīng)的槽函數(shù)相連,然后調(diào)用start()函數(shù)。接下來(lái),每隔一段時(shí)間,定時(shí)器便會(huì)發(fā)出一次timeout()信號(hào)。
更多用法這里就不講了,您可以自行參考官方文檔。比如如何停止、如何令定時(shí)器只運(yùn)行一次等。
二、在多線程中使用QTimer
1.錯(cuò)誤用法
您可能會(huì)這么做:
子類化QThread,在線程類中定義一個(gè)定時(shí)器,然后在run()方法中調(diào)用定時(shí)器的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("當(dāng)前線程id:") << QThread::currentThread();
}接下來(lái)在主線程中創(chuàng)建該線程對(duì)象,并調(diào)用它的start()方法:
m_pThread = new TestThread(this); m_pThread->start();
看似十分自然,沒(méi)有什么不妥,然而,編譯器將通知下面的錯(cuò)誤信息:
QObject::startTimer: Timers cannot be started from another thread
——定時(shí)器不能被其它線程start。
我們來(lái)分析一下:
剛開(kāi)始只有主線程一個(gè),TestThread的實(shí)例是在主線程中創(chuàng)建的,定時(shí)器在TestThread的構(gòu)造函數(shù)中,所以也是在主線程中創(chuàng)建的。
當(dāng)調(diào)用TestThread的start()方法時(shí),這時(shí)有兩個(gè)線程。定時(shí)器的start()方法是在另一個(gè)線程中,也就是TestThread中調(diào)用的。
創(chuàng)建和調(diào)用并不是在同一線程中,所以出現(xiàn)了錯(cuò)誤。
具體的原理可參考官方文檔——點(diǎn)我
每個(gè)QObject實(shí)例都有一個(gè)叫做“線程關(guān)系”(thread affinity)的屬性,或者說(shuō),它處于某個(gè)線程中。
默認(rèn)情況下,QObject處于創(chuàng)建它的線程中。
當(dāng)QObject接收隊(duì)列信號(hào)(queued signal)或者傳來(lái)的事件(posted event),槽函數(shù)或事件處理器將在對(duì)象所處的線程中執(zhí)行。
根據(jù)以上的原理,Qt使用計(jì)時(shí)器的線程關(guān)系(thread affinity)來(lái)決定由哪個(gè)線程發(fā)出timeout()信號(hào)。正因如此,你必須在它所處的線程中start或stop該定時(shí)器,在其它線程中啟動(dòng)定時(shí)器是不可能的。
2.正確用法一
在TestThread線程啟動(dòng)后創(chuàng)建定時(shí)器。
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.不能像下面這樣給定時(shí)器指定父對(duì)象
m_pTimer = new QTimer(this);
否則會(huì)出現(xiàn)以下警告:
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)
因?yàn)門estThread對(duì)象是在主線程中創(chuàng)建的,它的QObject子對(duì)象也必須在主線程中創(chuàng)建。所以不能指定父對(duì)象為TestThread。
2.必須要加上事件循環(huán)exec()
否則線程會(huì)立即結(jié)束,并發(fā)出finished()信號(hào)。
另外還有一點(diǎn)需要注意,與start一樣,定時(shí)器的stop也必須在TestThread線程中,否則會(huì)出錯(cuò)。
void TestThread::timeoutSlot()
{
m_pTimer->stop();
qDebug() << QString::fromLocal8Bit("當(dāng)前線程id:") << QThread::currentThread();
}上面的代碼將出現(xiàn)以下錯(cuò)誤:
QObject::killTimer: Timers cannot be stopped from another thread
綜上,子類化線程類的方法可行,但是不太好。
3.正確用法二
無(wú)需子類化線程類,通過(guò)信號(hào)啟動(dòng)定時(shí)器。
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);
}通過(guò)moveToThread()方法改變定時(shí)器所處的線程,不要給定時(shí)器設(shè)置父類,否則該函數(shù)將不會(huì)生效。
在信號(hào)槽連接時(shí),我們?cè)黾恿艘粋€(gè)參數(shù)——連接類型,先看看該參數(shù)可以有哪些值:
- Qt::AutoConnection:默認(rèn)值。如果接收者處于發(fā)出信號(hào)的線程中,則使用Qt::DirectConnection,否則使用Qt::QueuedConnection,連接類型由發(fā)出的信號(hào)決定。
- Qt::DirectConnection:信號(hào)發(fā)出后立即調(diào)用槽函數(shù),槽函數(shù)在發(fā)出信號(hào)的線程中執(zhí)行。
- Qt::QueuedConnection:當(dāng)控制權(quán)返還給接收者信號(hào)的事件循環(huán)中時(shí),開(kāi)始調(diào)用槽函數(shù)。槽函數(shù)在接收者的線程中執(zhí)行。
回到我們的例子,首先將定時(shí)器所處的線程改為新建的線程,然后連接信號(hào)槽,槽函數(shù)在定時(shí)器所處的線程中執(zhí)行。
到此這篇關(guān)于Qt實(shí)現(xiàn)線程與定時(shí)器的方法的文章就介紹到這了,更多相關(guān)Qt 線程與定時(shí)器 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Qt兩種定時(shí)器使用實(shí)現(xiàn)方式
- Qt定時(shí)器(QTimer)的3種使用方法
- Qt定時(shí)器類QTimer使用詳解與注意事項(xiàng)
- QT定時(shí)器事件的實(shí)現(xiàn)示例
- Qt使用事件與定時(shí)器實(shí)現(xiàn)字幕滾動(dòng)效果
- Qt實(shí)現(xiàn)定時(shí)器的兩種方法分享
- Qt實(shí)現(xiàn)高精度定時(shí)器
- Qt基于定時(shí)器實(shí)現(xiàn)動(dòng)圖展示效果
- Qt基礎(chǔ)開(kāi)發(fā)之Qt多線程類QThread與Qt定時(shí)器類QTimer的詳細(xì)方法與實(shí)例
- Qt定時(shí)器和隨機(jī)數(shù)詳解
- Qt中定時(shí)器 QTimerEvent 和 QTimer的使用
相關(guān)文章
利用Matlab實(shí)現(xiàn)迭代適應(yīng)點(diǎn)算法
道格拉斯-普克算法(Douglas–Peucker?algorithm,亦稱為拉默-道格拉斯-普克算法、迭代適應(yīng)點(diǎn)算法、分裂與合并算法)是將曲線近似表示為一系列點(diǎn),并減少點(diǎn)的數(shù)量的一種算法。本文將利用Matlab實(shí)現(xiàn)這一算法,需要的可以參考一下2022-04-04
c++ 單線程實(shí)現(xiàn)同時(shí)監(jiān)聽(tīng)多個(gè)端口
這篇文章主要介紹了c++ 單線程實(shí)現(xiàn)同時(shí)監(jiān)聽(tīng)多個(gè)端口的方法,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下2021-03-03
C++ throw關(guān)鍵字實(shí)現(xiàn)拋出異常和異常規(guī)范
本文主要介紹了C++ throw關(guān)鍵字實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
C++實(shí)現(xiàn)LeetCode(49.群組錯(cuò)位詞)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(49.群組錯(cuò)位詞),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語(yǔ)言實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
vs2017智能感知錯(cuò)誤解決代碼標(biāo)紅但編譯通過(guò)問(wèn)題
這篇文章主要介紹了vs2017智能感知錯(cuò)誤代碼標(biāo)紅但編譯通過(guò)問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
C語(yǔ)言數(shù)組入門之?dāng)?shù)組的聲明與二維數(shù)組的模擬
這篇文章主要介紹了C語(yǔ)言數(shù)組入門之?dāng)?shù)組的聲明與二維數(shù)組的模擬,數(shù)組學(xué)習(xí)的同時(shí)也要相應(yīng)理解C語(yǔ)言指針的作用,需要的朋友可以參考下2015-12-12

