Qt多线程学习:创建多线程
【為什么要用多線(xiàn)程?】
傳統(tǒng)的圖形用戶(hù)界面應(yīng)用程序都只有一個(gè)執(zhí)行線(xiàn)程,并且一次只執(zhí)行一個(gè)操作。如果用戶(hù)從用戶(hù)界面中調(diào)用一個(gè)比較耗時(shí)的操作,當(dāng)該操作正在執(zhí)行時(shí),用戶(hù)界面通常會(huì)凍結(jié)而不再響應(yīng)。這個(gè)問(wèn)題可以用事件處理和多線(xiàn)程來(lái)解決。
?
【Linux有線(xiàn)程的概念嗎?】
傳統(tǒng)的UNIX系統(tǒng)也支持線(xiàn)程的概念,但一個(gè)進(jìn)程里只允許有一個(gè)線(xiàn)程,這樣多線(xiàn)程就是多進(jìn)程。Linux下的Posix線(xiàn)程(pthreads)是一種輕量級(jí)的進(jìn)程的移植性實(shí)現(xiàn),線(xiàn)程的調(diào)度由內(nèi)核完成,每個(gè)線(xiàn)程都有自己的編號(hào)。如果使用線(xiàn)程,總體消耗的系統(tǒng)資源較少,線(xiàn)程間通信也比較容易,在工程中推薦使用線(xiàn)程。
?
【使用多線(xiàn)程有什么好處?】
?
【Qt中創(chuàng)建線(xiàn)程的方法】
只需要子類(lèi)化QThread并重新實(shí)現(xiàn)它的run()函數(shù)就可以了。run()是個(gè)純虛函數(shù),是線(xiàn)程執(zhí)行的入口,在run()里出現(xiàn)的代碼將會(huì)在另外線(xiàn)程中被執(zhí)行。run()函數(shù)是通過(guò)start()函數(shù)來(lái)實(shí)現(xiàn)調(diào)用的。?
?
【實(shí)例】
下面一個(gè)例子給出了在應(yīng)用程序中除了主線(xiàn)程外,還提供了線(xiàn)程A和B。如果單擊窗口中的按鈕“Start A”,Qt的控制臺(tái)就會(huì)連續(xù)輸出字母“A”,此時(shí)按鈕“Start A”被刷新為“Stop A”。再單擊按鈕“Start B”,控制臺(tái)會(huì)交替輸出字母“A”和“B”。如果再單擊按鈕“Stop A”,則控制臺(tái)只輸出字母“B”。
thread.h代碼
1 #ifndef THREAD_H 2 #define THREAD_H 3 4 #include <QThread> 5 #include <iostream> 6 7 class Thread : public QThread 8 { 9 Q_OBJECT 10 public: 11 Thread(); 12 void setMessage(QString message); 13 void stop(); 14 15 protected: 16 void run(); 17 void printMessage(); 18 19 private: 20 QString messageStr; 21 volatile bool stopped; 22 }; 23 24 #endif // THREAD_H注:
- stopped被聲明為易失性變量(volatile variable,斷電或中斷時(shí)數(shù)據(jù)丟失而不可再恢復(fù)的變量類(lèi)型),這是因?yàn)椴煌木€(xiàn)程都需要訪(fǎng)問(wèn)它,并且我們也希望確保它能在任何需要的時(shí)候都保持最新讀取的數(shù)值。如果省略關(guān)鍵字volatile,則編譯器就會(huì)對(duì)這個(gè)變量的訪(fǎng)問(wèn)進(jìn)行優(yōu)化,可能導(dǎo)致不正確的結(jié)果。
?
thread.cpp代碼
1 #include "thread.h" 2 #include <QDebug> 3 4 Thread::Thread() 5 { 6 stopped = false; 7 } 8 9 void Thread::run() 10 { 11 while(!stopped) 12 { 13 printMessage(); 14 } 15 stopped = false; 16 } 17 18 void Thread::stop() 19 { 20 stopped = true; 21 } 22 23 void Thread::setMessage(QString message) 24 { 25 messageStr = message; 26 } 27 28 void Thread::printMessage() 29 { 30 qDebug()<<messageStr; 31 sleep(1); 32 }注:
- QTread提供了一個(gè)terminate()函數(shù),該函數(shù)可以再一個(gè)線(xiàn)程還在運(yùn)行的時(shí)候就終止它的執(zhí)行,但不推薦用terminate(),因?yàn)閠erminate()不會(huì)立刻終止這個(gè)線(xiàn)程,該線(xiàn)程何時(shí)終止取決于操作系統(tǒng)的調(diào)度策略,也就是說(shuō),它可以隨時(shí)停止線(xiàn)程執(zhí)行而不給這個(gè)線(xiàn)程自我清空的機(jī)會(huì)。更安全的方法是用stopped變量和stop()函數(shù),如例子所示。
- 調(diào)用setMessage()讓第一個(gè)線(xiàn)程每隔1秒打印字母“A”,而讓第二個(gè)線(xiàn)程每隔1秒打印字母“B”。
- 線(xiàn)程會(huì)因?yàn)檎{(diào)用printf()而持有一個(gè)控制I/O的鎖,多個(gè)線(xiàn)程同時(shí)調(diào)用printf()在某些情況下回造成控制臺(tái)輸出阻塞,而用qDebug()作為控制臺(tái)輸出一般不會(huì)出現(xiàn)上述問(wèn)題。
?
threaddialog.h代碼
1 #ifndef THREADDIALOG_H 2 #define THREADDIALOG_H 3 4 #include <QPushButton> 5 #include <QDialog> 6 #include <QCloseEvent> 7 #include "thread.h" 8 9 class ThreadDialog : public QDialog 10 { 11 Q_OBJECT 12 13 public: 14 ThreadDialog(QWidget *parent=0); 15 16 protected: 17 void closeEvent(QCloseEvent *event); 18 19 private slots: 20 void startOrStopThreadA(); 21 void startOrStopThreadB(); 22 void close(); 23 24 private: 25 Thread threadA; 26 Thread threadB; 27 QPushButton *threadAButton; 28 QPushButton *threadBButton; 29 QPushButton *quitButton; 30 }; 31 32 #endif // THREADDIALOG_H?
threaddialog.cpp代碼
1 #include "threaddialog.h" 2 3 ThreadDialog::ThreadDialog(QWidget *parent) : QDialog(parent) 4 { 5 threadA.setMessage("A"); 6 threadB.setMessage("B"); 7 8 threadAButton = new QPushButton(tr("Start A"), this); 9 threadAButton->setGeometry(10, 30, 80, 30); 10 threadBButton = new QPushButton(tr("Start B"),this); 11 threadBButton->setGeometry(110, 30, 80, 30); 12 quitButton = new QPushButton(tr("Quit"), this); 13 quitButton->setGeometry(210, 30, 80, 30); 14 quitButton->setDefault(true); 15 16 connect(threadAButton, SIGNAL(clicked()), this, SLOT(startOrStopThreadA())); 17 connect(threadBButton, SIGNAL(clicked()), this, SLOT(startOrStopThreadB())); 18 connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); 19 } 20 21 void ThreadDialog::startOrStopThreadA() 22 { 23 if(threadA.isRunning()) 24 { 25 threadAButton->setText(tr("Stop A")); 26 threadA.stop(); 27 threadAButton->setText(tr("Start A")); 28 } 29 else 30 { 31 threadAButton->setText(tr("Start A")); 32 threadA.start(); 33 threadAButton->setText(tr("Stop A")); 34 } 35 } 36 37 void ThreadDialog::startOrStopThreadB() 38 { 39 if(threadB.isRunning()) 40 { 41 threadBButton->setText(tr("Stop B")); 42 threadB.stop(); 43 threadBButton->setText(tr("Strat B")); 44 } 45 else 46 { 47 threadBButton->setText(tr("Start B")); 48 threadB.start(); 49 threadBButton->setText(tr("Stop B")); 50 } 51 } 52 53 void ThreadDialog::closeEvent(QCloseEvent *event) 54 { 55 threadA.stop(); 56 threadB.stop(); 57 threadA.wait(); 58 threadB.wait(); 59 event->accept(); 60 } 61 62 void ThreadDialog::close() 63 { 64 exit(0); 65 }注:
- startOrStopA的邏輯是:當(dāng)單擊A的按鈕時(shí),如果系統(tǒng)判斷到有線(xiàn)程A在運(yùn)行中,就把A的按鈕刷新為“Stop A”,表示可以進(jìn)行stop A的動(dòng)作,并停止線(xiàn)程A的運(yùn)行,再將A的按鈕刷新為“Start A”。否則,如果線(xiàn)程A沒(méi)有運(yùn)行,就把按鈕刷新為表示可以運(yùn)行的“Start A”,啟動(dòng)線(xiàn)程A,然后將A按鈕刷新為“Stop A”。
- 當(dāng)不用Qt設(shè)計(jì)器時(shí),new一個(gè)button出來(lái),需要指定一個(gè)父類(lèi),比如this,否則運(yùn)行程序,窗口里沒(méi)有按鈕。
- new了多個(gè)按鈕或控件,需要用setGeometry來(lái)確定它們的大小和位置,否則前面的被后面的覆蓋,最終看到的是最后一個(gè)按鈕。setGeometry的前2個(gè)參數(shù)是相對(duì)于窗口的坐標(biāo)位置,后兩個(gè)參數(shù)是按鈕的長(zhǎng)寬。
- 單擊Quit或關(guān)閉窗口,就停止所有正在運(yùn)行的線(xiàn)程,并且在調(diào)用函數(shù)QCloseEvent::accept()之前等待它們完全結(jié)束,這樣就可以確保應(yīng)用程序是以一種原始清空的狀態(tài)退出的。
- 如果沒(méi)有62~65行的重新定義close函數(shù),使進(jìn)程完全退出。否則點(diǎn)擊Quit按鈕或叉號(hào)退出窗口后,進(jìn)程依然駐留在系統(tǒng)里。
?
main.cpp代碼
1 #include "threaddialog.h" 2 #include <QApplication> 3 4 int main(int argc, char *argv[]) 5 { 6 QApplication app(argc, argv); 7 ThreadDialog *threaddialog = new ThreadDialog; 8 //threaddialog->exec(); //threaddialog->exec(); threaddialog->show();//否則關(guān)閉界面后線(xiàn)程不能正常退出 9 return app.exec();10 }注:
- 在GUI程序中,主線(xiàn)程也被稱(chēng)為GUI線(xiàn)程,因?yàn)樗俏ㄒ灰粋€(gè)允許執(zhí)行GUI相關(guān)操作的線(xiàn)程。必須在創(chuàng)建一個(gè)QThread之前創(chuàng)建QApplication對(duì)象。
總結(jié)
以上是生活随笔為你收集整理的Qt多线程学习:创建多线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Qt中定时器使用的两种方法
- 下一篇: Qt 进程 QProcess