1. 程式人生 > >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_H widegt.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");     else         qDebug("[1] Thread is not running");       if(thread2->isRunning())         qDebug("[2] Thread is running");     else         qDebug("[2] Thread is not running"); }   void widget::btn_isFinished() {     if(thread1->isFinished())         qDebug("[1] Thread Finish");     else         qDebug("[1] Thread not Finish");       if(thread2->isFinished())         qDebug("[2] Thread Finish");     else         qDebug("[2] Thread not Finish"); }   widget::~widget() {} 輸出結果:

程式碼分析: 1.QThread訊號與槽 訊號:終止執行緒例項執行傳送訊號(void finished)、啟動執行緒例項傳送訊號(void started)、結束執行緒例項傳送訊號(void terminated) 槽:執行緒中止執行槽(finished)、執行緒啟動槽(start)、執行緒結束槽(terminate) 2.QThread優先順序 QThread通過函式setPriority()設定優先順序。 4.關於QDebug的一點思考                                               

VS2010開發Qt,怎麼顯示qDebug資訊(新增DOS視窗):

---------------------  作者:沈子恆  來源:CSDN  原文:https://blog.csdn.net/shenziheng1/article/details/60873227?utm_source=copy  版權宣告:本文為博主原創文章,轉載請附上博文連結!