QtC++ 多线程编程详细笔记

发布于:2025-06-27 ⋅ 阅读:(18) ⋅ 点赞:(0)

在QtC++中,有两种多线程编程的方式,接下来详细记录这两种使用方式。做做笔记,方便查看。

单线程的情况

在主线程中执行耗时操作,会导致界面卡住,未响应状态,影响用户体验。多线程异步操作可以解决这一问题。

void Widget::on_pushButton_clicked()
{
    int num = 0;
    while(1)
    {
        num ++;
        if (num == 100000000)
        {
            break;
        }
        ui->label->setNum(num);
    }
}

在这里插入图片描述

第一种方式 重写run函数

run适合于执行较简单的任务,run中不适合放置太多的代码。

  • 第一步
    创建线程类的子类,让其继承自QThread
    在这里插入图片描述
    创建好类文件后需要修改2个地方
    在这里插入图片描述
    在这里插入图片描述

  • 第二步
    重写父类的run方法

  • 第三步
    调用start方法启动子线程

注意:

  1. 子线程中不可操作ui线程,比如子线程中设置标签的显示

示例代码

以下示例展示了ui线程怎么向子线程发送数据以及子线程怎么向ui线程发送数据,以实现主线程与子线程的通信。

生成随机数的子线程

// 生成随机数的子线程
class Generate : public QThread
{
    Q_OBJECT
public:
    explicit Generate(QObject *parent = nullptr);

protected:
    void run() override;



public slots:
	// 接收从ui界面发送过来的生成随机数的个数需求
    void recvNumber(int num);


signals:
	// 将生成的随机数发送给主线程
    void sendArray(QVector<int> num);


private:
    ///
    /// \brief m_num   存放从主线程发送过来的数值,数值为生成随机数的个数
    /// 当主线程发送过来这个数据后,子线程要有一个槽函数来处理主线程发送过来的这个数值
    ///
    ///
    int m_num;

};
Generate::Generate(QObject *parent) : QThread(parent)
{

}
// 重写run方法
void Generate::run()
{
    qDebug() << "生成随机数的线程地址为:" << QThread::currentThread();
    QVector<int> list;
    QElapsedTimer startTime;
    startTime.start();
    for (int i = 0; i < m_num; i++)
    {
        // 存储到动态数组当中  随机生成100000 以内的随机数
        list.push_back(qrand() % 100000);
    }
    // 计算执行所消耗的时间
    int ms = startTime.elapsed();
    qDebug() << "生成" << m_num << "个随机数用时" << ms << "毫秒";
    emit sendArray(list);
}
// 接收数据
void Generate::recvNumber(int num)
{
    this->m_num = num;

}

主线程代码

gen = new Generate;
// 连接子线程接收随机数个数的信号和槽
connect(this, &Widget::needRandomNumberCount, gen, &Generate::recvNumber); 
// 连接主界面接收子线程发送数据的信号和槽
connect(gen, &Generate::sendArray, this, &Widget::recvRandomNumber);
void Widget::recvRandomNumber(QVector<int> list)
{
    for (int i = 0; i < list.size(); i++)
    {
        ui->listWidget->addItem(QString::number(list.at(i)));
    }
}

第二种方式 任务函数

  • 第一步
    创建一个任务类,该类必须继承自QObject

  • 第二步
    在这个类中添加公共的成员函数,函数体就是执行的任务

  • 第三步
    在主线程中创建一个QThread对象,这就是子线程对象

  • 第四步
    在主线程中创建工作的类对象
    注意: 不能给创建的任务对象指定父对象,致命提示!!!

  • 第五步
    将任务对象移动到创建的子线程对象中

  • 第六步
    调用start方法启动子线程,
    注意: 这个时候线程启动了,但是移动到线程中的对象并没有工作

  • 第七步
    调用任务对象的工作函数,让这个函数开始执行,任务才算真的执行了

示例代码

创建任务类,继承自QObject

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>

// 任务类
class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

public:
//    void recvNum(int num);
    // 任务函数 可以携带参数的
    void working(int num);
    void startedSlots();

signals:
    void sendArray(QVector<int> num);

private:
    int m_num;
};

#endif // MYTHREAD_H

子线程生成随机数的任务函数,可以有多个任务函数

#include "mythread.h"
#include <QElapsedTimer>
#include <QDebug>
#include <QThread>
MyThread::MyThread(QObject *parent) : QObject(parent)
{

}

void MyThread::working(int num)
{
    qDebug() << "生成随机数的线程地址:" << QThread::currentThread();
    QVector<int> list;
    QElapsedTimer time;
    time.start();
    for (int i = 0; i < num; i++)
    {
        list.push_back(qrand() % 100000);
    }
    int ms = time.elapsed();
    qDebug() << "生成" << num << "个随机数总共用时:" << ms << "毫秒";
    emit sendArray(list);
}

void MyThread::startedSlots()
{
    qDebug() << "1";
}

主线程 ui线程

#include "widget.h"
#include "ui_widget.h"
#include "mythread.h"
#include <QThread>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 创建任务类对象
    MyThread *work = new MyThread;
    // 创建子线程对象
    thread = new QThread;
    // 将任务对象移动到子线程中
    work->moveToThread(thread);
    connect(thread, &QThread::started, work, &MyThread::startedSlots);
    connect(this, &Widget::starting, work, &MyThread::working);
    connect(work, &MyThread::sendArray, this, [=](QVector<int> list){
       for (int i = 0; i < list.size(); i++)
       {
           ui->listWidget->addItem(QString::number(list.at(i)));
       }
    });
}

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


void Widget::on_pushButton_clicked()
{
    emit starting(10000);
    thread->start();
}

运行结果
在这里插入图片描述
上边的代码没有释放和清理资源。这是很重要的一步操作

释放和清理资源

界面销毁时有一个destoryed信号,连接该信号进行释放即可

 connect(this, &Widget::destroyed, this, [=]()
{
        // 安全退出
        thread->quit();
        thread->wait();
        thread->deleteLater();
        work->deleteLater();
});

网站公告

今日签到

点亮在社区的每一天
去签到