Qt-线程的使用

时间:2024-01-27 08:33:16

1  简介

参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=74

使用多线程的好处:提高应用程序响应速度、使多CPU更加高效、改善程序结构。

在Qt中使用QThread来管理线程。Qt中使用线程时,需要自己实现一个thread的类。

2  测试说明

(1)基本使用

功能说明如下:

 

 

 工程文件有:

 

 

 mythread.h和mythread.cpp是自定义的线程类,需要改为继承自QThread,QThread类有一个虚函数run(),它就是线程处理函数,我们需要重写它。当我们调用QThread的start()函数时,会间接的调用run()函数。

widget.h和widget.cpp是主窗口的代码。

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。