Qt-線程的使用

1  簡介

參考視頻:https://www.bilibili.com/video/BV1XW411x7NU?p=74多線程

使用多線程的好處:提升應用程序響應速度、使多CPU更加高效、改善程序結構。ide

在Qt中使用QThread來管理線程。Qt中使用線程時,須要本身實現一個thread的類。函數

2  測試說明

(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
View Code

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 }
View Code

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
View Code

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 }
View Code

運行測試:

能夠看到計數了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
View Code

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 }
View Code

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
View Code

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 }
View Code

須要說明如下幾點:

(1)建立自定義線程變量時,不能指定父對象mythread=newMyThread;),由於調用moveToThread函數以後,變量在建立的線程中使用回收,而不是在主線程。

(2)子線程會睡眠1s就發送一個信號mySignal給主線程,主線程接收信號,在槽函數中將數值累加一,並在LCD上顯示。

(3)主線程經過信號和槽的方式調用子線程處理函數,主線程發送信號給子線程,槽函數就是子線程的線程處理函數。

(4)子線程setFlag()的做用是:關閉子線程時,quit()函數會等待子線程執行結束以後再回收,可是若是不設置標誌,while循環會一直執行,子線程也就沒有結束。

運行測試:

注意看打印的信息,能夠看到子線程和主線程的id。

多線程使用過程當中注意事項:

1)線程不能操做UI對象(從Qwidget直接或間接派生的窗口對象)

2)須要移動到子線程中處理的模塊類,建立的對象的時候不能指定父對象。

相關文章
相關標籤/搜索