返回顶部

Qt-线程的使用

1  简介

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

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

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

多线程使用过程中注意事项:

1)线程不能操作UI对象(从Qwidget直接或间接派生的窗口对象)

2)需要移动到子线程中处理的模块类,创建的对象的时候不能指定父对象。

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是自定义的线程类,继承自QObject,和前一个例子不一样。

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。

(3)线程绘图

功能:子线程在处理函数中绘制图像,然后通过信号把绘制的图像传给主线程,主线程接收到图像之后调用update()函数更新绘图事件,进行图像的绘制。

也就是子线程把图片给画好了,传给主线程,主线程在窗口中绘制出来。主窗口中有一个按钮,按一次,绘制一次图像。

工程文件有:

mythread.h和mythread.cpp是自定义的线程类;widget.h和widget.cpp是主窗口的代码。

mythread.h代码:

 1 #ifndef MYTHREAD_H
 2 #define MYTHREAD_H
 3 
 4 #include <QObject>
 5 #include <QThread>
 6 #include <QImage>
 7 #include <QPainter>
 8 
 9 class MyThread : public QObject
10 {
11     Q_OBJECT
12 public:
13     explicit MyThread(QObject *parent = nullptr);
14     //线程处理函数
15     void drawImage();
16 
17 signals:
18     void updateImage(QImage image);
19 
20 public slots:
21 };
22 
23 #endif // MYTHREAD_H
View Code

mythread.c代码:

 1 #include "mythread.h"
 2 #include <QPoint>
 3 #include <QPen>
 4 #include <QBrush>
 5 
 6 MyThread::MyThread(QObject *parent) : QObject(parent)
 7 {
 8 
 9 }
10 
11 void MyThread::drawImage()
12 {
13     //绘图设备
14     QImage image(500, 500, QImage::Format_ARGB32);
15     //定义画家,指定绘图设别
16     QPainter painter(&image);
17 
18     //画笔
19     QPen pen;
20     pen.setWidth(5);  //设定画笔的宽度
21     //把画笔交给画家
22     painter.setPen(pen);
23     //定义画刷
24     QBrush brush;
25     brush.setStyle(Qt::SolidPattern);  //设定样式
26     brush.setColor(Qt::green);  //设定颜色
27     //把画刷交给画家
28     painter.setBrush(brush);
29 
30     //定义五个点
31     QPoint a[] = {
32         QPoint(qrand()%500, qrand()%500),
33         QPoint(qrand()%500, qrand()%500),
34         QPoint(qrand()%500, qrand()%500),
35         QPoint(qrand()%500, qrand()%500),
36         QPoint(qrand()%500, qrand()%500)
37     };
38     //画多边形
39     painter.drawPolygon(a, 5);
40     //通过信号发送图片
41     emit updateImage(image);
42 }
View Code

widget.h代码:

 1 #ifndef WIDGET_H
 2 #define WIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QImage>
 6 #include "mythread.h"
 7 #include <QThread>
 8 
 9 namespace Ui {
10 class Widget;
11 }
12 
13 class Widget : public QWidget
14 {
15     Q_OBJECT
16 
17 public:
18     explicit Widget(QWidget *parent = 0);
19     ~Widget();
20     //重写绘图事件
21     void paintEvent(QPaintEvent *event);
22     void getImage(QImage tmp); //槽函数
23     void dealClose();
24 
25 private:
26     Ui::Widget *ui;
27     QImage image;
28     MyThread *mythread = NULL;  //自定义线程
29     QThread *thread = NULL;
30 };
31 
32 #endif // WIDGET_H
View Code

widget.cpp代码:

 1 #include "widget.h"
 2 #include "ui_widget.h"
 3 #include <QPainter>
 4 #include <QThread>
 5 
 6 Widget::Widget(QWidget *parent) :
 7     QWidget(parent),
 8     ui(new Ui::Widget)
 9 {
10     ui->setupUi(this);
11 
12     //自定义类对象
13     mythread = new MyThread;
14     //创建子线程
15     thread = new QThread(this);
16     //把自定义线程添加到子线程
17     mythread->moveToThread(thread);
18     //启动子线程,但是没有启动线程处理函数
19     thread->start();
20     //线程处理函数必须通过signal-slot调用
21     connect(ui->pushButton, &QPushButton::clicked, mythread, &MyThread::drawImage);
22 
23     connect(mythread, &MyThread::updateImage, this, &Widget::getImage);
24 
25     connect(this, &Widget::destroyed, this, &Widget::dealClose);
26 }
27 
28 void Widget::getImage(QImage tmp)
29 {
30     image = tmp;
31     //更新窗口,间接调用paintEvent
32     update();
33 }
34 
35 void Widget::paintEvent(QPaintEvent *event)
36 {
37     //创建画家,指定绘图设备为窗口
38     QPainter painter(this);
39     painter.drawImage(50, 50, image);
40 }
41 
42 void Widget::dealClose()
43 {
44     //退出子线程
45     thread->quit();
46     //回收资源
47     thread->wait();
48     delete mythread;
49 }
50 
51 Widget::~Widget()
52 {
53     delete ui;
54 }
View Code

运行测试:

posted @ 2020-07-14 23:16  zhengcixi  阅读(177)  评论(0编辑  收藏