目錄
線程概念及官方文檔
?一、線程的創(chuàng)建:繼承方式
二、線程的創(chuàng)建:QObject 對(duì)象(moveToThread)
2.1 創(chuàng)建任務(wù)類
2.2 添加線程啟動(dòng)(定時(shí)器啟動(dòng))
2.3?添加線程啟動(dòng)(start信號(hào)啟動(dòng))
三、線程類的基本接口和使用
3.1啟動(dòng) 和終止線程
3.2 線程延遲
3.3 線程同步及通信方式
3.4 常用函數(shù)例舉
四、線程釋放
五、總結(jié)
線程概念及官方文檔
????????線程(英語(yǔ):thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。在Unix System V及SunOS中也被稱為輕量進(jìn)程(lightweight processes),但輕量進(jìn)程更多指內(nèi)核線程(kernel thread),而把用戶線程(user thread)稱為線程。這段話摘之網(wǎng)絡(luò)。
????????從下面的官方文檔可以看出,如果一個(gè)線程類要支持信號(hào)和槽,那么,該類要直接或者間接的繼承于QObject類,并且在類中要聲明宏:Q_OBJECT
????????好了,不說概念了,不喜歡說概念,直接開始說干貨。
?一、線程的創(chuàng)建:繼承方式
線程的創(chuàng)建方式:自定義一個(gè)類繼承于QThread,并且 重寫該類的run函數(shù),run函數(shù)中,就是 子線程要執(zhí)行任務(wù)。在創(chuàng)建線程后,要進(jìn)行啟動(dòng)線程 操作,下面是例子
class WorkerThread : public QThread
{
void run() override {
while(1){
qDebug()<<"hello";
QThread::sleep(1);
}
}
};
運(yùn)用的話簡(jiǎn)單,舉個(gè)例子,就能看到一秒中輸出一次 hello
WorkerThread *workerThread = new WorkerThread(this);
workerThread->start();//子線程要調(diào)用start啟動(dòng),否則,不會(huì)執(zhí)行
注意事項(xiàng):文章來源:http://www.zghlxwxcb.cn/news/detail-538533.html
? ? ? 1. 默認(rèn)情況下,void run() 只執(zhí)行一次,想要執(zhí)行多次,要手動(dòng)使用循環(huán)語(yǔ)句
? ? ? 2.?子線程一定要調(diào)用start啟動(dòng),否則,不會(huì)執(zhí)行
? ? ? 3.要想實(shí)現(xiàn)和其他線程通信,可使用信號(hào)的方式進(jìn)行通信,后面我上傳 緩沖區(qū)那篇文章會(huì)用到。
? ? ?實(shí)現(xiàn)的步驟基本就是
1.創(chuàng)建一個(gè)子類繼承于QThread,并且重寫run函數(shù)
2.創(chuàng)建子線程對(duì)象
3.啟動(dòng)子線程 ---- start()
二、線程的創(chuàng)建:QObject 對(duì)象(moveToThread)
這種方式我重點(diǎn)講,前面的方式對(duì)于我來說,沒有這種方式好用,在開發(fā)中,我經(jīng)常用這種方式。
步驟分為三步,第一步創(chuàng)建用于運(yùn)行任務(wù)的類,第二步創(chuàng)建線程,并在類中添加線程,第三步啟動(dòng)
下面講一下詳細(xì)步驟。
2.1 創(chuàng)建任務(wù)類
創(chuàng)建一個(gè)類繼承于QObject,并且聲明Q_OBJECT,這是規(guī)定的。定義一個(gè)槽函數(shù),用于線程的執(zhí)行。下面用代碼演示一下
class threadhid : public QObject
{
Q_OBJECT
public:
explicit threadhid(QObject *parent = nullptr);
~threadhid();
void stopRun(void); //用于退出
public:
bool m_Run ; //退出線程的標(biāo)志位
public slots:
void Receive(); //用于線程循環(huán)
};
函數(shù)實(shí)現(xiàn):
這是最簡(jiǎn)單的模板,具體的功能可以自己添加上去。簡(jiǎn)單說明一下參數(shù),m_Run標(biāo)志位是為了更好的退出這個(gè)槽函數(shù),再釋放線程。stopRun()函數(shù)是可以通過類接口進(jìn)行釋放線程,更加安全,不會(huì)隨意退出,更好管理。
threadhid::threadhid(QObject *parent ): QObject(parent)
{
}
threadhid::~threadhid()
{
}
void threadhid::stopRun(void)
{
m_bRun = false;
}
void threadhid::Receive()
{
m_Run = true;
while(true) {
if(!m_Run)
{
qDebug()<<"退出";
break;
}
QThread::msleep(200);
qDebug()<<"hello 大寶犯疆土...";
}
}
2.2 添加線程啟動(dòng)(定時(shí)器啟動(dòng))
?上面是創(chuàng)建任務(wù),現(xiàn)在是給與這個(gè)類一個(gè)線程,達(dá)到分離的效果。
??第一步創(chuàng)建類線程
?QThread * thread_Hid = new QThread;? ? ? ? ?
??第二步創(chuàng)建任務(wù)類
?threadhid* ?USB_Handle = new threadhid() ; ?//創(chuàng)建任務(wù)類
第三步添加到線程中
USB_Handle->moveToThread(&thread_Hid);?
第四步?線程釋放 ?
?connect(&thread_Hid, &QThread::finished, USB_Handle, &QObject::deleteLater);
注:這段代碼是用于連接一個(gè)線程對(duì)象 thread_Hid 的 finished() 信號(hào)和一個(gè) QObject 對(duì)象 USB_Thread 的 deleteLater() 槽函數(shù)的。 在多線程編程中,線程的生命周期可能會(huì)比較復(fù)雜,如果沒有正確地管理線程的生命周期,就可能會(huì)導(dǎo)致內(nèi)存泄漏或者程序崩潰等問題。因此,Qt 提供了一些機(jī)制來幫助管理線程的生命周期,其中之一就是通過在線程結(jié)束時(shí)自動(dòng)刪除線程對(duì)象來確保線程對(duì)象的正確釋放。 在上述代碼中,當(dāng) thread_Hid 線程結(jié)束時(shí),就會(huì)發(fā)出 finished() 信號(hào),這個(gè)信號(hào)被連接到 USB_Thread 對(duì)象的 deleteLater() 槽函數(shù)上,這樣當(dāng)線程結(jié)束時(shí),就會(huì)自動(dòng)調(diào)用 USB_Thread 對(duì)象的 deleteLater() 槽函數(shù),從而保證線程對(duì)象的正確釋放。 需要注意的是,當(dāng)使用 QObject::deleteLater() 槽函數(shù)時(shí),要確保所要?jiǎng)h除的對(duì)象是在其所屬線程的事件循環(huán)中被創(chuàng)建的,否則 deleteLater() 函數(shù)可能不會(huì)生效。
第五步 線程啟動(dòng)
thread_Hid.start();
第六步 創(chuàng)建單次定時(shí)器啟動(dòng)線程任務(wù)
timerHid = new QTimer(this);
connect(timerHid, &QTimer::timeout, USB_Handle, &threadhid::Receive);
timerHid->setSingleShot(true);
timerHid->start(1000);
為了方便大家復(fù)制和我以后某一天想起,直接貼完整代碼
threadhid* USB_Handle = new threadhid() ;
QThread * thread_Hid = new QThread; //線程
USB_Handle->moveToThread(&thread_Hid);添加到線程中
//線程釋放
connect(&thread_Hid, &QThread::finished, USB_Handle, &QObject::deleteLater);
thread_Hid.start();//線程啟動(dòng)
//定時(shí)器單次運(yùn)行
timerHid = new QTimer(this);
connect(timerHid, &QTimer::timeout, USB_Handle, &threadhid::Receive);
timerHid->setSingleShot(true);
timerHid->start(1000);
2.3?添加線程啟動(dòng)(start信號(hào)啟動(dòng))
為什么說這個(gè)呢,就是想簡(jiǎn)單的記錄一下多種啟動(dòng)方式,上面的那種是我自己用的,我覺得可以讓時(shí)間更加可靠,而下面這種是一啟動(dòng)就運(yùn)行的。2.3和2.2效果是一樣,不用重復(fù)寫。
connect(thread_Hid, &QThread::started, USB_Handle, &threadhid::Receive); // 連接線程槽
thread_Hid->start();? ? ? ?//啟動(dòng)
三、線程類的基本接口和使用
3.1啟動(dòng) 和終止線程
3.1.1退出線程
void?quit() //退出一個(gè)線程循環(huán),可以起作 停止線程
3.1.2 啟動(dòng)線程
start(QThread::Priority priority = InheritPriority)//啟動(dòng)線程
3.1.3 退出線程
terminate()//終止線程
區(qū)別:
terminate() 函數(shù)是一個(gè)強(qiáng)制終止程序的函數(shù),它可以立即終止應(yīng)用程序并釋放相關(guān)資源,但這種方式可能會(huì)導(dǎo)致一些未定義的行為,例如可能會(huì)導(dǎo)致未處理的異常和內(nèi)存泄漏等問題。因此,建議只在出現(xiàn)緊急情況時(shí)使用 terminate() 函數(shù)。
quit() 函數(shù)是一個(gè)優(yōu)雅地終止程序的函數(shù),它會(huì)告訴事件循環(huán)停止運(yùn)行,并等待所有線程結(jié)束后再退出應(yīng)用程序。它會(huì)釋放所有的資源,包括 GUI 界面等。因此,建議在正常情況下使用 quit() 函數(shù)來終止應(yīng)用程序。
3.2 線程延遲
3.3 線程同步及通信方式
同步機(jī)制的目的是為了保護(hù)數(shù)據(jù)或者代碼段,在多線程中,每次只允許一個(gè)線程來進(jìn)行訪問。線程同步方式:互斥鎖 讀寫鎖 信號(hào)量等
QMutex、QMutexLocker, QReadWriteLock, QSemaphore, and QWaitCondition.具體用法就不說了,復(fù)制一下百度,這次重點(diǎn)介紹線程。
通信方式:
1.信號(hào)和槽?
2.QMetaObject::invokeMethod():可以在不同線程之間異步地調(diào)用一個(gè)函數(shù),實(shí)現(xiàn)線程間的通信。
3.QThread::event()和QCoreApplication::postEvent():可以用于在線程之間發(fā)送事件和處理事件。
3.4 常用函數(shù)例舉
-
start() 函數(shù):用于啟動(dòng)線程,它會(huì)調(diào)用線程的 run() 函數(shù),并將線程標(biāo)記為“運(yùn)行中”。
-
run() 函數(shù):是線程的主函數(shù),可以在該函數(shù)中執(zhí)行需要在線程中運(yùn)行的代碼。
-
wait() 函數(shù):用于等待線程結(jié)束,它會(huì)使當(dāng)前線程阻塞,直到目標(biāo)線程結(jié)束為止。
-
quit() 函數(shù):用于終止線程,它會(huì)發(fā)送一個(gè)退出信號(hào)給線程,并等待線程結(jié)束后才返回。
-
isRunning() 函數(shù):用于檢查線程是否正在運(yùn)行中。
-
setPriority() 函數(shù):用于設(shè)置線程的優(yōu)先級(jí),Qt 支持以下幾種優(yōu)先級(jí):IdlePriority、LowestPriority、LowPriority、NormalPriority、HighPriority、HighestPriority、TimeCriticalPriority。
-
sleep() 函數(shù):用于使當(dāng)前線程睡眠一段時(shí)間,單位是毫秒。
-
msleep() 函數(shù):用于使當(dāng)前線程睡眠一段時(shí)間,單位是微秒。
-
yieldCurrentThread() 函數(shù):用于將 CPU 時(shí)間讓給其他線程。
-
currentThreadId() 函數(shù):用于獲取當(dāng)前線程的 ID。
-
event() 函數(shù):用于處理事件,QThread 中的事件有兩種:QThread::CustomEventType 和 QThread::Quit。
-
exit() 函數(shù):用于退出線程,并釋放線程所占用的資源。
-
finished() 信號(hào):當(dāng)線程結(jié)束時(shí)會(huì)發(fā)出該信號(hào),可以通過該信號(hào)來實(shí)現(xiàn)線程的清理工作。
需要注意的是,在多線程編程中,要避免使用一些不安全的操作,如訪問全局變量、使用未初始化的指針、使用不可重入的函數(shù)等,否則可能會(huì)導(dǎo)致程序出現(xiàn)不可預(yù)知的錯(cuò)誤。在多線程編程中需要特別注意線程安全問題。
四、線程釋放
線程的釋放也是跟重要的,作為一個(gè)合格的工程師不能只管申請(qǐng)不管釋放,有始有終對(duì)吧。
第一步釋放類內(nèi)資源,并退出死循環(huán)
第二步退出事件循環(huán)
第三步等待線程退出
第四步釋放內(nèi)存
USB_Handle->stopRun(); //先退出死循環(huán) m_Run = false;
thread_Hid.quit(); //退出事件循環(huán)
thread_Hid.wait(); //等待線程退出
// QThread::terminate()//也可以,但是上面的方式更加溫柔
delete thread_Hid;
delete USB_Handle ;
大功告成。
五、總結(jié)
-
QThread
可以通過繼承QThread
類并重寫run
函數(shù)來實(shí)現(xiàn)自定義線程類。 -
QThread
還提供了信號(hào)槽機(jī)制,使得線程之間的通信更加方便。 -
QThread
通過exec
函數(shù)實(shí)現(xiàn)事件循環(huán),當(dāng)線程沒有事件時(shí),exec
函數(shù)會(huì)阻塞線程。
注意事項(xiàng):
- 在
QThread
中創(chuàng)建的對(duì)象默認(rèn)情況下都會(huì)與QThread
線程關(guān)聯(lián),應(yīng)該使用moveToThread
函數(shù)將其移動(dòng)到新的線程中。 - 不應(yīng)該直接調(diào)用
QThread
的run
函數(shù),應(yīng)該通過start
函數(shù)來啟動(dòng)線程。 -
QThread
對(duì)象應(yīng)該在主線程中創(chuàng)建,并且只能在主線程中刪除。 - 為了避免資源泄露,應(yīng)該在線程執(zhí)行完畢后將其刪除。
Qt線程的總結(jié)暫時(shí)就這么多,我寫的博客偏向于實(shí)際應(yīng)用型,概念不多強(qiáng)調(diào),只為更加方便的使用,減少?gòu)U話。如果本篇給與你開發(fā)或多或少的幫助,希望可以給個(gè)點(diǎn)贊激勵(lì)一下哈。文章來源地址http://www.zghlxwxcb.cn/news/detail-538533.html
到了這里,關(guān)于QT創(chuàng)建線程的方法:一步步教你創(chuàng)建和啟動(dòng)線程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!