網(wǎng)站首頁 編程語言 正文
一、定時器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類定義一個定時器,它可以不停重復(fù),也可以只進行一次便停止。
使用起來也很簡單:
QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(1000);
創(chuàng)建一個QTimer對象,將信號timeout()與相應(yīng)的槽函數(shù)相連,然后調(diào)用start()函數(shù)。接下來,每隔一段時間,定時器便會發(fā)出一次timeout()信號。
更多用法這里就不講了,您可以自行參考官方文檔。比如如何停止、如何令定時器只運行一次等。
二、在多線程中使用QTimer
1.錯誤用法
您可能會這么做:
子類化QThread,在線程類中定義一個定時器,然后在run()方法中調(diào)用定時器的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(); }
接下來在主線程中創(chuàng)建該線程對象,并調(diào)用它的start()方法:
m_pThread = new TestThread(this); m_pThread->start();
看似十分自然,沒有什么不妥,然而,編譯器將通知下面的錯誤信息:
?QObject::startTimer: Timers cannot be started from another thread?
——定時器不能被其它線程start。
我們來分析一下:
剛開始只有主線程一個,TestThread的實例是在主線程中創(chuàng)建的,定時器在TestThread的構(gòu)造函數(shù)中,所以也是在主線程中創(chuàng)建的。
當(dāng)調(diào)用TestThread的start()方法時,這時有兩個線程。定時器的start()方法是在另一個線程中,也就是TestThread中調(diào)用的。
創(chuàng)建和調(diào)用并不是在同一線程中,所以出現(xiàn)了錯誤。
具體的原理可參考官方文檔——點我
每個QObject實例都有一個叫做“線程關(guān)系”(thread affinity)的屬性,或者說,它處于某個線程中。
默認情況下,QObject處于創(chuàng)建它的線程中。
當(dāng)QObject接收隊列信號(queued signal)或者傳來的事件(posted event),槽函數(shù)或事件處理器將在對象所處的線程中執(zhí)行。
根據(jù)以上的原理,Qt使用計時器的線程關(guān)系(thread affinity)來決定由哪個線程發(fā)出timeout()信號。正因如此,你必須在它所處的線程中start或stop該定時器,在其它線程中啟動定時器是不可能的。
2.正確用法一
在TestThread線程啟動后創(chuàng)建定時器。
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);
否則會出現(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)
因為TestThread對象是在主線程中創(chuàng)建的,它的QObject子對象也必須在主線程中創(chuàng)建。所以不能指定父對象為TestThread。
2.必須要加上事件循環(huán)exec()
否則線程會立即結(jié)束,并發(fā)出finished()信號。
另外還有一點需要注意,與start一樣,定時器的stop也必須在TestThread線程中,否則會出錯。
void TestThread::timeoutSlot() { m_pTimer->stop(); qDebug() << QString::fromLocal8Bit("當(dāng)前線程id:") << QThread::currentThread(); }
上面的代碼將出現(xiàn)以下錯誤:
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()方法改變定時器所處的線程,不要給定時器設(shè)置父類,否則該函數(shù)將不會生效。
在信號槽連接時,我們增加了一個參數(shù)——連接類型,先看看該參數(shù)可以有哪些值:
- Qt::AutoConnection:默認值。如果接收者處于發(fā)出信號的線程中,則使用Qt::DirectConnection,否則使用Qt::QueuedConnection,連接類型由發(fā)出的信號決定。
- Qt::DirectConnection:信號發(fā)出后立即調(diào)用槽函數(shù),槽函數(shù)在發(fā)出信號的線程中執(zhí)行。
- Qt::QueuedConnection:當(dāng)控制權(quán)返還給接收者信號的事件循環(huán)中時,開始調(diào)用槽函數(shù)。槽函數(shù)在接收者的線程中執(zhí)行。
回到我們的例子,首先將定時器所處的線程改為新建的線程,然后連接信號槽,槽函數(shù)在定時器所處的線程中執(zhí)行。
原文鏈接:https://www.cnblogs.com/hellovenus/p/qt_thread_timer.html
相關(guān)推薦
- 2022-06-20 React函數(shù)組件與類組件使用及優(yōu)劣對比_React
- 2022-11-16 解決Oracle模擬事務(wù)提交、表鎖,處理表鎖問題_oracle
- 2022-11-15 python運行cmd命令行的3種方法總結(jié)_python
- 2023-11-21 linux sudo:/etc/sudoers 中第 11 行附近有解析錯誤 sudo:no val
- 2022-03-26 jquery對元素的基本操作實例分析_jquery
- 2022-08-15 GoAccess對Nginx日志分析完美分析
- 2022-12-12 Docker制作tomcat鏡像并部署項目_docker
- 2023-02-04 GO語言并發(fā)之好用的sync包詳解_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支