zoukankan      html  css  js  c++  java
  • 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

    运行测试:

  • 相关阅读:
    【JAVA与C#比较】其它
    C#和java之间的一些差异与共性
    C#与Java的语法差异
    关于npm本地安装模块包(node_modules),安装不了的问题
    vue
    vue
    vue
    vue
    v
    vue -model
  • 原文地址:https://www.cnblogs.com/mrlayfolk/p/13303035.html
Copyright © 2011-2022 走看看