參考視頻:https://www.bilibili.com/video/BV1XW411x7NU?p=74多線程
使用多線程的好處:提升應用程序響應速度、使多CPU更加高效、改善程序結構。ide
在Qt中使用QThread來管理線程。Qt中使用線程時,須要本身實現一個thread的類。函數
(1)基本使用測試
功能說明以下:ui
工程文件有:this
mythread.h和mythread.cpp是自定義的線程類,須要改成繼承自QThread,QThread類有一個虛函數run(),它就是線程處理函數,咱們須要重寫它。spa
當咱們調用QThread的start()函數時,會間接的調用run()函數。線程
widget.h和widget.cpp是主窗口的代碼。code
mythread.h的代碼:視頻
1 #ifndef MYTHREAD_H 2 #define MYTHREAD_H 3 4 #include <QObject> 5 #include <QThread> 6 7 class MyThread : public QThread 8 { 9 Q_OBJECT 10 public: 11 explicit MyThread(QObject *parent = nullptr); 12 13 signals: 14 void isDone(); 15 16 protected: 17 //QThread的虛函數,線程處理函數 18 //不能直接調用,經過start()間接調用 19 void run(); 20 21 public slots: 22 }; 23 24 #endif // MYTHREAD_H
mythread.cpp代碼:
1 #include "mythread.h" 2 3 MyThread::MyThread(QObject *parent) : QThread(parent) 4 { 5 6 } 7 8 void MyThread::run() 9 { 10 //很複雜的數據處理,須要耗時5s 11 sleep(5); 12 //發送處理完成信號 13 emit isDone(); 14 }
widget.h代碼:
1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 #include <QTimer> //定時器 6 #include "mythread.h" //線程 7 8 namespace Ui { 9 class Widget; 10 } 11 12 class Widget : public QWidget 13 { 14 Q_OBJECT 15 16 public: 17 explicit Widget(QWidget *parent = 0); 18 ~Widget(); 19 20 void dealTimeout(); //定時器處理函數 21 void dealThread(); //處理子線程發來的信號 22 void stopThread(); //中止線程 23 24 private slots: 25 void on_pushButton_start_clicked(); 26 27 private: 28 Ui::Widget *ui; 29 30 QTimer *timer = NULL; 31 MyThread *thread = NULL; 32 }; 33 34 #endif // WIDGET_H
widget.cpp代碼:
1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QThread> 4 #include <QDebug> 5 6 Widget::Widget(QWidget *parent) : 7 QWidget(parent), 8 ui(new Ui::Widget) 9 { 10 ui->setupUi(this); 11 12 timer = new QTimer(this); 13 //分配空間 14 thread = new MyThread(this); 15 16 //只要定時器啓動,自動觸發timerout()信號 17 connect(timer, &QTimer::timeout, this, &Widget::dealTimeout); 18 //接收子線程發送的isDone信號並處理 19 connect(thread, &MyThread::isDone, this, &Widget::dealThread); 20 //當按窗口右上角x時(關閉窗口),觸發 21 connect(this, &Widget::destroyed, this, &Widget::stopThread); 22 } 23 24 Widget::~Widget() 25 { 26 delete ui; 27 } 28 29 void Widget::dealTimeout() 30 { 31 static int i = 0; 32 i++; 33 //設定lcd的值 34 ui->lcdNumber->display(i); 35 } 36 37 void Widget::dealThread() 38 { 39 //處理完數據後,關閉定時器 40 timer->stop(); 41 qDebug() << "timer turn off!!!"; 42 } 43 44 void Widget::stopThread() 45 { 46 //中止線程 47 thread->quit(); 48 //等待線程處理完事情 49 thread->wait(); 50 } 51 52 void Widget::on_pushButton_start_clicked() 53 { 54 if (timer->isActive() == false) { 55 timer->start(100); 56 } 57 //啓動線程,處理數據 58 thread->start(); 59 }
運行測試:
能夠看到計數了45次以後(每次100ms),定時器中止了,表示接收到了線程發送的信號(線程睡眠5s以後發出的)。可能會有疑問爲何不是50次(恰好5s),那是由於咱們先啓動定時器,在去啓動線程,這個過程須要花費時間。
(2)多線程的使用
多線程的實現模型以下,記下來就行了:
先給出實現的代碼,後面再介紹。
工程文件有:
mythread.h和mythread.cpp是自定義的線程類,繼承自QThread,和前一個例子不同。
widget.h和widget.cpp是主窗口的代碼。
mythread.h代碼:
1 #ifndef MYTHREAD_H 2 #define MYTHREAD_H 3 4 #include <QObject> 5 6 class MyThread : public QObject 7 { 8 Q_OBJECT 9 public: 10 explicit MyThread(QObject *parent = nullptr); 11 12 //線程處理函數 13 void myTimerout(); 14 //設置flag,用於判斷是否結束線程處理函數的while循環 15 void setFlag(bool flag = true); 16 17 signals: 18 void mySignal(); 19 20 public slots: 21 22 private: 23 bool isStop; 24 }; 25 26 #endif // MYTHREAD_H
mythread.c代碼:
1 #include "mythread.h" 2 #include <QThread> 3 #include <QDebug> 4 5 MyThread::MyThread(QObject *parent) : QObject(parent) 6 { 7 isStop = false; 8 } 9 10 void MyThread::myTimerout() 11 { 12 while (isStop == false) { 13 QThread::sleep(1); 14 emit mySignal(); 15 qDebug() << "子線程號:" << QThread::currentThread(); 16 if (true == isStop) { 17 break; 18 } 19 } 20 } 21 22 void MyThread::setFlag(bool flag) 23 { 24 isStop = flag; 25 }
widget.h代碼:
1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 #include "mythread.h" 6 #include <QThread> 7 8 namespace Ui { 9 class Widget; 10 } 11 12 class Widget : public QWidget 13 { 14 Q_OBJECT 15 16 public: 17 explicit Widget(QWidget *parent = 0); 18 ~Widget(); 19 20 void dealsignal(); 21 void dealclose(); 22 signals: 23 //啓動子線程的信號 24 void startThreadSignal(); 25 26 private slots: 27 void on_pushButton_start_clicked(); 28 29 void on_pushButton_stop_clicked(); 30 31 private: 32 Ui::Widget *ui; 33 MyThread *mythread = NULL; 34 QThread *thread = NULL; 35 }; 36 37 #endif // WIDGET_H
widget.cpp代碼:
1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QDebug> 4 5 Widget::Widget(QWidget *parent) : 6 QWidget(parent), 7 ui(new Ui::Widget) 8 { 9 ui->setupUi(this); 10 //動態分配空間,不能指定父對象 11 mythread = new MyThread; 12 //建立子線程 13 thread = new QThread(this); 14 //把自定義的線程加入到子線程中 15 mythread->moveToThread(thread); 16 17 //處理子線程發送的信號 18 connect(mythread, &MyThread::mySignal, this, &Widget::dealsignal); 19 qDebug() << "主線程號:" << QThread::currentThread(); 20 //發送信號給子線程,經過信號和槽調用子線程的線程處理函數 21 connect(this, &Widget::startThreadSignal, mythread, &MyThread::myTimerout); 22 //關閉主窗口 23 connect(this, &Widget::destroyed, this, &Widget::dealclose); 24 } 25 26 Widget::~Widget() 27 { 28 delete ui; 29 } 30 31 void Widget::dealsignal() 32 { 33 static int i = 0; 34 i++; 35 ui->lcdNumber->display(i); 36 } 37 38 void Widget::dealclose() 39 { 40 mythread->setFlag(true); 41 thread->quit(); 42 thread->wait(); 43 delete mythread; 44 } 45 46 void Widget::on_pushButton_start_clicked() 47 { 48 if (thread->isRunning() == true) { 49 return; 50 } 51 //啓動線程,可是沒有啓動線程處理函數 52 thread->start(); 53 mythread->setFlag(false); 54 //不能直接調用線程處理函數 55 //直接調用致使線程處理函數和主線程在同一個線程 56 //只能經過信號和槽調用 57 emit startThreadSignal(); 58 } 59 60 void Widget::on_pushButton_stop_clicked() 61 { 62 if (thread->isRunning() == false) { 63 return; 64 } 65 mythread->setFlag(true); 66 thread->quit(); 67 thread->wait(); 68 }
須要說明如下幾點:
(1)建立自定義線程變量時,不能指定父對象(mythread=newMyThread;),由於調用moveToThread函數以後,變量在建立的線程中使用回收,而不是在主線程。
(2)子線程會睡眠1s就發送一個信號mySignal給主線程,主線程接收信號,在槽函數中將數值累加一,並在LCD上顯示。
(3)主線程經過信號和槽的方式調用子線程處理函數,主線程發送信號給子線程,槽函數就是子線程的線程處理函數。
(4)子線程setFlag()的做用是:關閉子線程時,quit()函數會等待子線程執行結束以後再回收,可是若是不設置標誌,while循環會一直執行,子線程也就沒有結束。
運行測試:
注意看打印的信息,能夠看到子線程和主線程的id。
多線程使用過程當中注意事項:
1)線程不能操做UI對象(從Qwidget直接或間接派生的窗口對象)
2)須要移動到子線程中處理的模塊類,建立的對象的時候不能指定父對象。