Qt修炼手册11_多线程编程和QThread类
生活随笔
收集整理的這篇文章主要介紹了
Qt修炼手册11_多线程编程和QThread类
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.事件循環
學習QT多線程編程之前,有必要先熟悉事件循環的概念。 先看一個單線程界面程序的主函數代碼: int main(int argc, char* argv[]) {QApplication app(argc, argv);// 構造主窗口對象并顯示MainWindow w;w.show(); // 進入事件循環return app.exec(); }在程序初始化完成后,主線程進入main()函數開始執行應用代碼。一般地,我們在主線程上構建界面對象,然后進入事件循環以處理控件繪制、用戶輸入、系統輸出等消息。這就是我們通常說的事件驅動模型。 這里需要提一個問題,為什么需要多線程編程?多線程編程旨在提高人機交互的感受。主線程承擔著用戶交互的重任,當在主線程上運行費時的代碼時,就會影響用戶的正常操作。所以我們常把一些費時費力的計算工作移出主線程,開辟新的線程來運行之。 QThread是QT中用于線程管理的類,調用一個QThread對象的start()方法時,會創建一個新的線程并執行它的run()方法。默認地,run()會調用exec()方法進入自己的消息循環中。如下圖所示:
上圖中有主線程、工作線程都是執行事件循環,并且注意到主線程內部含有thr、w、objs這些QObject對象(這些對象都是在主線程上創建的)。主線程的事件循環負責檢測這些對象是否有消息要處理,有的話則調用對象的slot方法。可以使用QObject::moveToThread方法將某個對象移到其他線程中,譬如:
class Worker : public QObject {Q_OBJECT… }void someFunc() {QThread thr = new QThread;Worker worker = new Worker;worker->moveToThread(thr);thr->start();… }如果在主線程上調用someFunc(),則thr和worker在創建后關聯在主線程上,當調用worker-> moveToThread()后,worker對象關聯到了新的線程中,如圖所示:
2.QThread類
2.1 類基礎
QThread類可以不受平臺影響而實現線程。QThread提供在程序中可以控制和管理現成的多種成員函數和信號/槽。通過QThread類的成員函數start()啟動線程。 class Worker : public QObject {Q_OBJECT public slots:void doWork(){......} };void MyObject::putWorkerInThread() {Worker *worker = new Worker;QThread *workerThread = new QThread(this);connect(workerThread, &QThread::started, worker, &Worker::doWork);connect(workerThread, &QThread::finished,worker, &Worker::deleteLater);worker->moveToThread(workerThread);//開始進行事件循環,并發射信號workerThread->start();workerThread->start(); }上述代碼中,Worker類的槽與啟動分離線程時發送的信號、線程終止時發送的信號相關聯。如果我們啟動了線程,就會調用Worker類的槽函數doWork()。換言之,如果運行putWorkerInThread()的函數start(),那么就相當于發送了線程的啟動信號(QThread::started),根據定義的信號-槽,程序就會調用與函數connect()相關聯的Worker類的槽函數doWorker()。如果結束線程,則自動發送QThread::finished信號,發送此信號后,立即釋放Worker使用過的線程內存區域。
QThread通過信號函數started()和finished()通知開始和結束,并查看線程狀態。可以確認究竟是使用函數isfinished()信號終止線程,還是使用函數isRunning()啟動線程。使用函數exit()和quit()可以結束線程。
2.2 多線程初步
如果使用多線程,有時需要等到所有線程終止。此時,使用函數wait()即可。線程中,使用成員函數sleep()、msleep()、usleep()可以暫停秒、毫秒及微秒單位的線程。3.QThread使用的內存區域
QThread使用的內存區域分為線程私有區域和共享內存區域。線程內部使用的寄存器區域只能在線程內部共享。共享數據區域可以訪問其他線程。 線程內部使用的寄存器和棧區域如下圖所示:線程內部共享區域雖然不能訪問其他線程,但棧區域可以在線程間共享。因此,如果實現多種線程訪問棧區域,需要注意互斥體,讀寫鎖等線程的安全性。
3.多線程編程實例與解析
widget.h #ifndef WIDGET_H #define WIDGET_H#include <QtWidgets/QWidget> #include "ui_widget.h" #include <QThread> #include <QPushButton> #include <QMutex> class MyThread : public QThread {Q_OBJECT public:MyThread(int num); private:bool threadStop;int number;QMutex mutex; public:void stop(); protected:void run(); }; / class widget : public QWidget {Q_OBJECT public:widget(QWidget *parent = 0);~widget();MyThread *thread1;MyThread *thread2; private slots:void btn_start();void btn_stop();void btn_isRunning();void btn_isFinished(); private:Ui::widgetClass ui; };#endif // WIDGET_Hwidegt.cpp#include "widget.h" MyThread::MyThread(int num) { number = num; }void MyThread::stop() {threadStop = true;qDebug("[%d] Thread stop", number); }void MyThread::run() {threadStop = false;int i = 0;while(!threadStop){mutex.lock();qDebug("[%d] MyThread %d", number, i);i++;sleep(1);mutex.unlock();} }/// widget::widget(QWidget *parent): QWidget(parent) {ui.setupUi(this);thread1 = new MyThread(1);thread2 = new MyThread(2);QPushButton *btn_start = new QPushButton("START", this);btn_start->setGeometry(10, 10, 80, 40);QPushButton *btn_stop = new QPushButton("STOP", this);btn_stop->setGeometry(100, 10, 80, 40);QPushButton *btn_isRunning = new QPushButton("IsRunning", this);btn_isRunning->setGeometry(200, 10, 100, 40);QPushButton *btn_isFinished = new QPushButton("IsFinished", this);btn_isFinished->setGeometry(310, 10, 100, 40);connect(btn_start, SIGNAL(clicked()), this, SLOT(btn_start()));connect(btn_stop, SIGNAL(clicked()), this, SLOT(btn_stop()));connect(btn_isRunning, SIGNAL(clicked()), this, SLOT(btn_isRunning()));connect(btn_isFinished, SIGNAL(clicked()), this, SLOT(btn_isFinished())); }void widget::btn_start() {thread1->start();thread2->start(); }void widget::btn_stop() {thread1->stop();thread2->stop(); }void widget::btn_isRunning() {if(thread1->isRunning())qDebug("[1] Thread is running");elseqDebug("[1] Thread is not running");if(thread2->isRunning())qDebug("[2] Thread is running");elseqDebug("[2] Thread is not running"); }void widget::btn_isFinished() {if(thread1->isFinished())qDebug("[1] Thread Finish");elseqDebug("[1] Thread not Finish");if(thread2->isFinished())qDebug("[2] Thread Finish");elseqDebug("[2] Thread not Finish"); }widget::~widget() {} 輸出結果: 代碼分析: 1.QThread信號與槽
- 信號:終止線程實例運行發送信號(void finished)、啟動線程實例發送信號(void started)、結束線程實例發送信號(void terminated)
- 槽:線程中止運行槽(finished)、線程啟動槽(start)、線程結束槽(terminate)
4.關于QDebug的一點思考 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
VS2010開發Qt,怎么顯示qDebug信息(添加DOS窗口): ??總結
以上是生活随笔為你收集整理的Qt修炼手册11_多线程编程和QThread类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈飞鸽传书实现原理
- 下一篇: HTML5参考手册