Qt 中最经典、最常用的多线程通信场景

实现步骤

  1. 创建工作类 (Worker):在工作线程中处理数据的对象。

  2. 创建线程对象 (QThread):用来托管工作对象。

  3. 连接信号槽

    • 主线程 -> 工作线程 :连接一个主窗口发出的信号工作对象的槽,用于传递数据。

    • 工作线程 -> 主线程 :连接工作对象发出的信号主窗口的槽,用于返回结果、更新UI。

  4. 移动工作对象:将工作对象移动到新线程。

  5. 启动线程:启动线程的事件循环。

  6. 按钮点击 :在按钮点击的槽函数中,发射那个用于传递数据的信号。

完整代码示例

1. 工作类 (worker.h)

cpp 复制代码
// worker.h
#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QString>
#include <QDebug>
#include <QThread>

class Worker : public QObject
{
    Q_OBJECT

public:
    explicit Worker(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    // 这个槽函数专门用来接收主线程发来的数据并进行处理
    void handleDataFromMainThread(const QString &data) {
        qDebug() << "工作线程ID:" << QThread::currentThreadId();
        qDebug() << "收到主线程发来的数据:" << data;

        // 模拟一个耗时的数据处理过程
        QString result;
        for (int i = 0; i < data.length(); ++i) {
            QThread::msleep(200); // 模拟处理每个字符需要时间
            result.prepend(data[i]); // 做一个简单的反转操作作为处理示例
            emit progress(i+1, data.length()); // 发送处理进度
        }

        // 处理完成,发射信号将结果发回主线程
        emit dataProcessed(result);
    }

signals:
    // 处理进度信号 (当前进度, 总数)
    void progress(int current, int total);
    // 处理完成信号 (结果)
    void dataProcessed(const QString &result);
};

#endif // WORKER_H

2. 主窗口头文件 (mainwindow.h)

cpp 复制代码
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

// 新增一个信号,用于向工作线程发送数据
signals:
    void sendDataToWorker(const QString &data);

private slots:
    // 按钮点击的槽函数
    void on_pushButtonStart_clicked();

    // 接收工作线程发回的信号的槽函数
    void updateProgress(int current, int total);
    void handleResult(const QString &result);

private:
    Ui::MainWindow *ui;
    QThread *workerThread;
};

#endif // MAINWINDOW_H

3. 主窗口实现 (mainwindow.cpp) - 核心

cpp 复制代码
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h" // 包含工作类头文件
#include <QThread>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , workerThread(new QThread(this)) // 线程对象的父对象是主窗口,生命周期由其管理
{
    ui->setupUi(this);

    // 创建工作者对象,此时它还在主线程
    Worker *worker = new Worker;

    // !!! 关键步骤:将工作者对象移动到新线程 !!!
    worker->moveToThread(workerThread);

    // !!! 核心连接:主线程 -> 工作线程 !!!
    // 当主窗口发射 sendDataToWorker 信号时, worker 的 handleDataFromMainThread 槽函数会被调用
    // 因为 worker 已移动到新线程,这个连接会自动使用 QueuedConnection,保证线程安全
    connect(this, &MainWindow::sendDataToWorker, worker, &Worker::handleDataFromMainThread);

    // 连接:工作线程 -> 主线程 (用于更新UI)
    connect(worker, &Worker::progress, this, &MainWindow::updateProgress);
    connect(worker, &Worker::dataProcessed, this, &MainWindow::handleResult);

    // 连接线程结束信号,用于自动清理对象
    connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
    connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);

    // 启动线程(启动事件循环)
    workerThread->start();

    // 初始化UI
    ui->progressBar->setValue(0);
}

MainWindow::~MainWindow()
{
    // 优雅退出线程
    if (workerThread && workerThread->isRunning()) {
        workerThread->quit();
        workerThread->wait();
    }
    delete ui;
}

// 按钮点击事件
void MainWindow::on_pushButtonStart_clicked()
{
    QString inputData = ui->lineEditInput->text();
    if (inputData.isEmpty()) {
        return;
    }

    // 禁用按钮,防止重复点击
    ui->pushButtonStart->setEnabled(false);
    ui->progressBar->setValue(0);

    // !!! 核心操作:发射信号,将数据发送到工作线程 !!!
    qDebug() << "主线程ID:" << QThread::currentThreadId() << ",发射信号,数据:" << inputData;
    emit sendDataToWorker(inputData);
}

// 更新进度条
void MainWindow::updateProgress(int current, int total)
{
    int percent = (current * 100) / total;
    ui->progressBar->setValue(percent);
    ui->labelStatus->setText(QString("处理中: %1/%2").arg(current).arg(total));
}

// 显示处理结果
void MainWindow::handleResult(const QString &result)
{
    ui->textEditResult->setText(result);
    ui->labelStatus->setText("处理完成!");
    ui->pushButtonStart->setEnabled(true); // 重新启用按钮
}
相关推荐
孞㐑¥17 分钟前
算法——BFS
开发语言·c++·经验分享·笔记·算法
MZ_ZXD0012 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
A星空1233 小时前
一、Linux嵌入式的I2C驱动开发
linux·c++·驱动开发·i2c
凡人叶枫3 小时前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发
会叫的恐龙3 小时前
C++ 核心知识点汇总(第六日)(字符串)
c++·算法·字符串
小糯米6014 小时前
C++顺序表和vector
开发语言·c++·算法
独望漫天星辰4 小时前
C++ 多态深度解析:从语法规则到底层实现(附实战验证代码)
开发语言·c++
王老师青少年编程5 小时前
2024年信奥赛C++提高组csp-s初赛真题及答案解析(阅读程序第3题)
c++·题解·真题·csp·信奥赛·csp-s·提高组
凡人叶枫5 小时前
C++中输入、输出和文件操作详解(Linux实战版)| 从基础到项目落地,避坑指南
linux·服务器·c语言·开发语言·c++
CSDN_RTKLIB5 小时前
使用三方库头文件未使用导出符号情景
c++