Qt---多线程

一、多线程介绍

        通常情况下,应用程序都是在一个线程中执行操作。但是当调用一个耗时操作(例如,大批量I/O或大量矩阵变换等CPU密集操作)时,用户界面常常会冻结。而使用多线程可以解决这一问题。

多线程的优势:

1. 提高应用程序响应速度

        当一个操作耗时很长时,整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等操作,而使用多线程技术可将耗时长的操作置于一个新的线程,避免以上问题。

2. 使多CPU系统更加有效

        当前线程数不大于CPU数目时,操作系统可以调度不同的线程运行于不同的CPU上。

3. 改善程序结构

        一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于代码的理解和维护。

未使用多线程造成阻塞结果示例:

通过sleep来模拟耗时操作,sleep过程中,窗口无响应,5秒之后开始输出:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTimer>//定时器头文件

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void dealTimerout();//定时器槽函数

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;

    QTimer *myTimer;//声明变量
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QThread>
#include<QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myTimer = new QTimer(this);

    //只要定时器启动,自动触发timeout
    connect(myTimer,QTimer::timeout,this,&Widget::dealTimerout);
}

void Widget::dealTimerout()
{
    static int i = 0;
    i++;
    //设置lcd的值
    ui->lcdNumber->display(i);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    //如果定时器没有工作
    if (myTimer->isActive() == false)
    {
        myTimer->start(100);
    }

    //非常复杂的数据处理,耗时较长
    QThread::sleep(5);

    //处理完数据后,关闭定时器
    //myTimer->stop();
    qDebug()<<"over";
}

执行代码,按下按钮start之后等待5秒之后输出如下所示: 

二、多线程的使用

线程1: 按下start立即输出,5秒之后暂停,屏幕输出it is over 

mythread.h 

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include<QThread>

class Mythread : public QThread
{
    Q_OBJECT
public:
    explicit Mythread(QObject *parent = nullptr);

protected:
    //QThread的虚函数
    //线程处理函数
    //不能直接调用,通过start()间接调用
    void run ();

signals:
    void isDone();

};

#endif // MYTHREAD_H

mythread.cpp

#include "widget.h"

Mythread::Mythread(QObject *parent) : QThread(parent)
{

}

void Mythread::run()
{
    //很复杂的数据处理
    //需要耗时5秒
    sleep(5);

    emit isDone();

}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTimer>//定时器头文件
#include"mythread.h"//线程头文件

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void dealTimerout();//定时器槽函数
    void dealDone();//线程结束槽函数
    void stopThread();//停止线程槽函数


private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;

    QTimer *myTimer;//声明变量
    Mythread *thread;//线程对象
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QThread>
#include<QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myTimer = new QTimer(this);

    //只要定时器启动,自动触发timeout
    connect(myTimer,QTimer::timeout,this,&Widget::dealTimerout);

    //分配空间
    thread = new Mythread(this);

    connect(thread,&Mythread::isDone,this,&Widget::dealDone);

    //当按窗口左上角x时,窗口触发destroyed()
    connect(this,&Widget::destroyed,this,&Widget::stopThread);
}

void Widget::stopThread()
{
    //停止线程
    thread->quit();
    //等待线程处理完手头工作
    thread->wait();
}

void Widget::dealDone()
{
    qDebug()<<"it is over";
    myTimer->stop();//关闭定时器
}

void Widget::dealTimerout()
{
    static int i = 0;
    i++;
    //设置lcd的值
    ui->lcdNumber->display(i);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    //如果定时器没有工作
    if (myTimer->isActive() == false)
    {
        myTimer->start(100);
    }

    //启动线程,处理数据
    thread->start();
}

输出如下所示:

线程2:通过按钮控制子线程执行和终止,并打印线程号

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void myTimeout();

    void setFlag(bool flag = true);

signals:
    void mySignal();

private:
    bool isStop;

};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
#include<QThread>
#include<QDebug>
#include<QMessageBox>

MyThread::MyThread(QObject *parent) : QObject(parent)
{
    isStop = false;
}

void MyThread::myTimeout()
{
    while(isStop == false)
    {
        QThread::sleep(1);
        emit mySignal();
        //QMessageBox::aboutQt(NULL);

        qDebug()<<"子主线程号:"<<QThread::currentThread();

        if(true == isStop)
        {
            break;
        }
    }
}

void MyThread::setFlag(bool flag)
{
    isStop = flag;
}

mywidget.ui

mywidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include"mythread.h"
#include<QThread>

QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget(QWidget *parent = nullptr);
    ~MyWidget();

    void dealSignal();
    void dealClose();

signals:
    void startThread();//启动子线程的信号

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

private:
    Ui::MyWidget *ui;
    MyThread *myT;
    QThread *thread;
};
#endif // MYWIDGET_H

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDebug>


MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    //动态分配空间,不能指定父对象
    myT = new MyThread;

    //创建子线程
    thread = new QThread(this);

    //把自定义的线程加入到子线程
    myT->moveToThread(thread);

    connect(myT,&MyThread::mySignal,this,&MyWidget::dealSignal);

    qDebug()<<"主线程号:"<<QThread::currentThread();

    connect(this,&MyWidget::startThread,myT,&MyThread::myTimeout);

    connect(this,&MyWidget::startThread,this,&MyWidget::dealClose);
}

MyWidget::~MyWidget()
{
    delete ui;
}

void MyWidget::dealClose()
{
    on_pushButton_clicked();
}

void MyWidget::dealSignal()
{
    static int i =0;
    i++;
    ui->lcdNumber->display(i);
}


void MyWidget::on_pushButton_clicked()
{
    if(thread->isRunning() == true)
    {
        return;
    }

    //启动线程,但是没有启动线程处理函数
    thread->start();
    myT->setFlag(false);

    //不能直接调用线程处理函数
    //直接调用导致线程处理函数和主线程是在同一个线程
    //myT->myTimeout();

    //只能通过signa - slot方式调用
    emit startThread();

}

void MyWidget::on_pushButton_2_clicked()
{
    if(thread->isRunning() == false)
    {
        return;
    }

    myT->setFlag(true);
    thread->quit();
    thread->wait();
}

输出如下所示:

 

注意:

1. 线程函数内部不允许操作图形界面

2. connect()函数第五个参数的作用->连接方式:默认、队列、直接

        默认:如果是多线程,默认使用队列;如果是单线程,默认使用直接方式

        队列:槽函数所在的线程和接收者一样

        直接:槽函数所在线程和发送者一样

三、使用线程绘图 

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include<QImage>

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void drawImage();

signals:
    void updateImage(QImage temp);

};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
#include<QPainter>
#include<QPen>
#include<QBrush>

MyThread::MyThread(QObject *parent) : QObject(parent)
{

}

void MyThread::drawImage()
{
    //定义QImage绘图设备
    QImage image(500,500,QImage::Format_ARGB32);
    //定义画家
    QPainter p(&image);

    //定义画笔对象
    QPen pen;
    pen.setWidth(5);//设置宽度
    //把画笔交给画家
    p.setPen(pen);

    //定义画刷
    QBrush brush;
    brush.setStyle(Qt::SolidPattern);//设置样式
    brush.setColor(Qt::red);//设置颜色
    //把画刷交给画家
    p.setBrush(brush);

    //定义5个点
    QPoint a[] =
    {
        QPoint(qrand()%200,qrand()%200),
        QPoint(qrand()%200,qrand()%200),
        QPoint(qrand()%200,qrand()%200),
        QPoint(qrand()%200,qrand()%200),
        QPoint(qrand()%200,qrand()%200),
    };

    p.drawPolygon(a,5);

    //通过信号发送图片
    emit updateImage(image);
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include"mythread.h"
#include<QThread>
#include<QImage>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    //重写绘图事件
    void paintEvent(QPaintEvent *);

    void getImage(QImage);//槽函数
    void dealClose();//窗口关闭槽函数

private:
    Ui::Widget *ui;
    QImage image;
    MyThread *myT;//自定义线程对象
    QThread *thread;//子线程
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QThread>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //自定义类对象,分配空间,不可以指定父对象
    myT = new MyThread;

    //创建子线程
    thread = new QThread(this);

    //把自定义模块添加到子线程
    myT->moveToThread(thread);

    //启动子线程,但并没有启动线程处理函数
    thread->start();

    //线程处理函数必须通过singal-slot调用
    connect(ui->pushButton,&QPushButton::pressed,myT,&MyThread::drawImage);

    connect(myT,&MyThread::updateImage,this,&Widget::getImage);

    connect(this,&Widget::destroyed,this,&Widget::dealClose);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::dealClose()
{
    //退出线程
    thread->quit();
    //回收资源
    thread->wait();
    delete myT;
}

void Widget::getImage(QImage temp)
{
    image = temp;
    update();//更新窗口,间接调用paintEvent()
}

void Widget::paintEvent(QPaintEvent *)
{
    QPainter p(this);//创建画家,指定绘图设备为窗口
    p.drawImage(50,50,image);
}

输出如下所示: 

 

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值