Qt中实现多线程导出数据功能的四种方式小结

2025-08-06 21:50

本文主要是介绍Qt中实现多线程导出数据功能的四种方式小结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Qt中实现多线程导出数据功能的四种方式小结》在以往的项目开发中,在很多地方用到了多线程,本文将记录下在Qt开发中用到的多线程技术实现方法,以导出指定范围的数字到txt文件为例,展示多线程不同的实现方...

前言

在以往的项目开发中,在很多地方用到了多线程。针对不同的业务逻辑,需要使用不同的多线程实现方法,来达到优化项目的目的。本文记录下在Qt开发中用到的多线程技术实现方法,以导出指定范围的数字到txt文件为例,展示多线程不同的实现方式。

示例已上传到gittee,地址:https://gitee.com/zbylalalala1/qt_-thread-demo.git

导出文件的示例工具类

首先提供一个工具类,用于将指定范围的数字写入txt文件。

#ifndef UTILITIES_H
#define UTILITIES_H

#include <QString>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QDir>
#include <QDebug>
class Utilities
{
public:
    static bool writeNumbersToFile(int start, int end, const QString& prefix = "numbers")
    {
        if (start > end) {
            qDebug() << "起始数字不能大于结束数字";
            return false;
        }
        
        // 获取当前时间并格式化为文件名
        QDateTime currentTime = QDateTime::currentDateTime();
        QString timeString = currentTime.toString("yyyy-MM-dd_hh-mm-ss");
        QString fileName = QString("%1_%2_to_%3_%4.txt")
                          .arg(prefix)
                          .arg(start)
                          .arg(end)
                          .arg(timeString);
        
        // 创建文件对象
        QFile file(fileName);
        
        // 以写入模式打开文件
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
            qDebug() << "无法创建文件:" << fileName;
            return false;
        }
        
        // 创建文本流
        QTextStream out(&file);
        
        // 写入指定范围的数字
        int count = 0;
        for (int i = start; i <= end; ++i) {
            out << i;
            count++;
            
            // 每10个数字换行
            if (count % 10 == 0 || i == end) {
                out << "\n";
            } else {
                out << " "; // 数字之间用空格分隔
            }
        }
        
        // 关闭文件
        file.close();
        
        qDebug() << "成功写入文件:" << fileName;
        qDebug() << "文件路径:" << QDir::currentPath() + "/" + fileName;
        qDebug() << "写入数字范围:" << start << "到" << end << ",共" << (end - start + 1) << "个数字";
        
        return true;
    }
    
    // 获取当前工作目录
    static QString getCurrentPath()
    {
        return QDir::currentPath();
    }
    
    // 检查文件是否存在
    static bool fileExists(const QString& fileName)
    {
        QFile file(fileName);
        return file.exists();
    }
};

#endif // UTILITIES_H

QThread

使用QThread类来创建线程,是Qt中最简单的一种多线程实现方式,不过一般不建议使用,因为它的功能比较有限。

使用QThread的方式为:继承QThread并重写run()函数。

ExportThread.h 

#ifndef EXPORTTHREAD_H
#define EXPORTTHREAD_H

#include <QThread>
#include <QDebug>
#include "Utilities.h"

class ExportThread : public QThread
{
    Q_OBJECT

public:
    explicit ExportThread(QObject *parent = nullptr);
    
    // 设置导出参数
    void setExportParams(int start = 1, int end = 10000, const QString& prefix = "numbers");
    
protected:
    void run() override;
    
signals:
    void exportStarted();
    void exportFinished(bool success, const QString& message);
    void progressUpdate(int current, int total);
    
private:
    int m_start;
    int m_end;
    QString m_prefix;
};

#endif // EXPORTTHREAD_H

ExportThread.cpp

#include "ExportThread.h"
#include <QDateTime>
#include <QDir>

ExportThread::ExportThread(QObject *parent)
    : QThread(parent)
    , m_start(1)
    , m_end(10000)
    , m_prefix("numbers")
{
}

void ExportThread::setExportParams(int start, int end, const QString& prefix)
{
    m_start = start;
    m_end = end;
    m_prefix = prefix;
}

void ExportThread::run()
{
    qDebug() << "导出线程开始运行...";
    emit exportStarted();
    
    try {
        bool success = Utilities::writeNumbersToFile(m_start, m_end, m_prefix);
        if (success) {
            emit exportFinished(true, QString("文件导javascript出成功!范围:%1-%2").arg(m_start).arg(m_end));
      http://www.chinasem.cn  } else {
            emit exportFinished(false, "文件导出失败!");
        }
        qDebug() << "导出线程完成";
        
    } catch (const std::exception& e) {
        qDebug() << "导出过程中发生异常:" << e.what();
        emit exportFinished(false, QString("导出过程中发生异常: %1").arg(e.what()));
    }
}

使用方式:

ExportThread *exportThread = new ExportThread(this);
exportThread->setExportParams(1, 10000, "numbers");
exportThread->start();

QObject的moveToThread方法实现多线程

QObject的moveToThread方法可以将一个QObject对象移动到指定的线程中,实现多线程。

使用方式:

QObject *obj = new QObject();
QThread *thread = new QThread();
obj->moveToThread(thread);
thread->start();

示例:

FileExportWorker.h 

#ifndef FILEEXPORTWORKER_H
#define FILEEXPORTWORKER_H

#include <QObject>
#include "Utilities.h"

class FileExportWorker : public QObject
{
    Q_OBJECT
public:
    explicit FileExportWorker(QObject *parent = nullptr);
    void exportNumbers(int start, int end, const QString& prefix);

signals:
    void progressUpdated(int current, int total);
    void statusUpdated(const QString& status);

public slots:
};

#endif // FILEEXPORTWORKER_H

FileExportWorker.cpp

#include "FileExportWorker.h"
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include <QThread>
#include <QCoreApplication>

FileExportWorker::FileExportWorker(QObject *parent)
    : QObject(parent)
    , m_start(1)
    , m_end(10000)
    , m_prefix("numbers")
    , m_shouldStop(false)
{
}

void FileExportWorker::setExportParams(int start, int end, const QString& prefix)
{
    m_start = start;
    m_end = end;
    m_prefix = prefix;
}

void FileExportWorker::doExport()
{
    qDebug() << "Worker线程ID:" << QThread::currentThreadId();
    qDebug() << "开始导出任务...";
    
    m_shouldStop = false;
    emit exportStarted();
    emit statusUpdated("正在准备导出...");
    
    try {
        bool success = false;
        
        emit statusUpdated("使用自定义参数导出...");
        success = exportNumbersWithProgress();
        
        if (m_shouldStop) {
            emit exportFinished(false, "导出已被用户取消");
        } else if (success) {
            emit exportFinished(true, QString("文件导出成功!范围:%1-%2").arg(m_start).arg(m_end));
        } else {
            emit exportFinished(false, "文件导出失败!");
        }
        
    } catch (const std::exception& e) {
        qDebug() << "导出过程中发生异常:" << e.what();
        emit exportFinished(false, QString("导出过程中发生异常: %1").arg(e.what()));
    }
    
    qDebug() << "导出任务完成";
}

void FileExportWorker::stopExport()
{
    m_shouldStop = true;
    emit statusUpdated("正在停止导出...");
}

bool FileExportWorker::exportNumbersWithProgresspython()
{
    // 获取当前时间并格式化为文件名
    QDateTime currentTime = QDateTime::currentDateTime();
    QString timeString = currentTime.toString("yyyy-MM-dd_hh-mm-ss");
    QString fileName = QString("%1_%2_to_%3_%4.txt")
                      .arg(m_prefix)
                      .arg(m_start)
                      .arg(m_end)
                      .arg(timeString);
    
    // 创建文件对象
    QFile file(fileName);
    
    // 以写入模式打开文件
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qDebug() << "无法创建文件:" << fileName;
        return false;
    }
    
    // 创建文本流
    QTextStream out(&file);
    
    int total = m_end - m_start + 1;
    int count = 0;
    
    // 写入指定范围的数字
    for (int i = m_start; i <= m_end; ++i) {
        if (m_shouldStop) {
            file.close();
            QFile::remove(fileName); // 删除未完成的文件
            return false;
        }
        
        out << i;
        count++;
        
   js     // 每10个数字换行
        if (count % 10 == 0 || i == m_end) {
            out << "\n";
        } else {
            out << " "; // 数字之间用空格分隔
        }
        
        // 每处理100个数字发送一次进度更新
        if (count % 100 == 0 || i == m_end) {
            emit progressUpdated(count, total);
            emit statusUpdated(QString("已处理 %1/%2 个数字").arg(count).arg(total));
            // 让出CPU时间,允许其他操作
            QCoreApplication::processEvents();
        }
    }
    
    // 关闭文件
    file.close();
    
    qDebug() << "成功写入文件:" << fileName;
    qDebug() << "文件路径:" << QDir::currentPath() + "/" + fileName;
    qDebug() << "写入数字范围:" << m_start << "到" << m_end << ",共" << total << "个数字";
    
    return true;
}

QConcurrent实现多线程导出数据

QConcurrent是Qt提供的一个并发编程框架,用于简化多线程编程。它提供了一些方便的函数和类,用于在多个线程中执行任务。本示例通过QConcurrent实现导出任务,实现多线程导出数据。

使用方式:

QFuture<bool> future = QConcurrent::run(this, &FileExportWorker::exportNumbersWithProgress);

示例:

FileExportWorker.h 

#ifndef CONCURRENTEXPORTER_H
#define CONCURRENTEXPORTER_H

#include <QObject>
#include <QString>
#include <QFuture>
#include <QFutureWatcher>
#include <QtConcurrent>
#include "Utilities.h"

class ConcurrentExporter : public QObject
{
    Q_OBJECT

public:
    explicit ConcurrentExporter(QObject *parent = nullptr);
    
    // 开始导出任务
    void startExport(int start = 1, int end = 10000, const QString& prefix = "concurrent");
    
    // 取消导出任务
    void cancelExport();
    
    // 检查是否正在运行
    bool isRunning() const;

signals:
    void exportStarted();
    void exportFinished(bool success, const QString& message);

private slots:
    void onExportFinished();

private:
    QFutureWatcher<bool> *m_watcher;
    QFuture<bool> m_future;
    int m_start;
    int m_end;
    QString m_prefix;
};

#endif // CONCURRENTEXPORTER_H

FileExportWorker.cpp

#include "ConcurrentExporter.h"
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include <QThread>
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrentRun>

ConcurrentExporter::ConcurrentExporter(QObject *parent)
    : QObject(parent)
    , m_watcher(new QFutureWatcher<bool>(this))
    , m_start(1)
    , m_end(10000)
    , m_prefix("concurrent")
{
    // 连接QFutureWatcher的信号
    connect(m_watcher, &QFutureWatcher<bool>::finished, this, &ConcurrentExporter::onExportFinished);
}

void ConcurrentExporter::startExport(int start, int end, const QString& prefix)
{
    if (isRunning()) {
        qDebug() << "导出任务已在运行中";
        return;
    }
    
    m_start = start;
    m_end = end;
    m_prefix = prefix;
    
    qDebug() << "使用Qt Concurrent开始导出任务...";
    qDebug() << "当前线程ID:" << QThread::currentThreadId();
    
    emit exportStarted();
    
    m_future = QtConcurrent::run([=]() {
        qDebug() << "工作线程ID:" << QThread::currentThreadId();
        return Utilities::writeNumbersToFile(start, end, prefix);
    });
    // 设置QFutureWatcher监视QFuture
    m_watcher->setFuture(m_future);
}

void ConcurrentExporter::cancelExport()
{
    if (isRunning()) {
        m_future.cancel();
    }
}

bool ConcurrentExporter::isRunning() const
{
    return m_future.isRunning();
}

void ConcurrentExporter::onExportFinished()
{
    bool success = false;
    QString message;
    
    if (m_future.isCanceled()) {
        message = "导出任务已被取消";
    } else {
        try {
            success = m_future.result();
            if (success) {
                message = QString("文件导出成功!范围:%1-%2").arg(m_start).arg(m_end);
            } else {
                message = "文件导出失败!";
            }
        } catch (const std::exception& e) {
            message = QString("导出过程中发生异常: %1").arg(e.what());
        }
    }
    
    emit exportFinished(success, message);
    qDebug() << "Qt Concurrent导出任务完成:" << message;
}

QRunnable结合QThreadPool方法实现多线程导出数据

QRunnable是Qt提供的一个接口,用于在多线程中执行任务。QThreadPool是一个线程池,用于管理多个线程。本示例通过QRunnable接口实现导出任务,通过QThreadPool线程池管理线程,实现多线程导出数据。

使用方式:

QThreadPool *pool = QThreadPool::globalInstance();
RunnableExportTask *task = new RunnableExportTask(1, 10000, "numbers");
pool->start(task);

示例:

RunnableExportTask.h

#ifndef RUNNABLEEXPORTTASK_H
#define RUNNABLEEXPORTTASK_H

#include <QRunnable>
#include <QObject>
#include <QString>
#include <QDebug>
#include "Utilities.h"

// 由于QRunnable不继承QObject,我们需要一个信号发射器
class ExportTaskNotifier : public QObject
{
    Q_OBJECT

public:
    explicit ExportTaskNotifier(QObject *parent = nullptr) : QObject(parent) {}
    
    void emitStarted() { emit exportStarted(); }
    void emitFinished(bool success, const QString& message) { emit exportFinished(success, message); }
    void emitProgress(const QString& status) { emit progressUpdated(status); }

signals:
    void exportStarted();
    void exportphpFinished(bool success, const QString& message);
    void progressUpdated(const QString& status);
};

class RunnableExportTask : public QRunnable
{
public:
    explicit RunnableExportTask(int start = 1, int end = 10000, const QString& prefix = "runnable");
    
    // 设置通知器,用于发送信号
    void setNotifier(ExportTaskNotifier *notifier);
    
    // 设置导出参数
    void setExportParams(int start, int end, const QString& prefix);
    
    // QRunnable接口实现
    void run() override;
    
private:
    int m_start;
    int m_end;
    QString m_prefix;
    ExportTaskNotifier *m_notifier;
};

#endif // RUNNABLEEXPORTTASK_H

RunnableExportTask.cpp

#include "RunnableExportTask.h"
#include <QThread>
#include <QDebug>

RunnableExportTask::RunnableExportTask(int start, int end, const QString& prefix)
    : m_start(start)
    , m_end(end)
    , m_prefix(prefix)
    , m_notifier(nullptr)
{
    // 设置任务完成后自动删除
    setAutoDelete(true);
}

void RunnableExportTask::setNotifier(ExportTaskNotifier *notifier)
{
    m_notifier = notifier;
}

void RunnableExportTask::setExportParams(int start, int end, const QString& prefix)
{
    m_start = start;
    m_end = end;
    m_prefix = prefix;
}

void RunnableExportTask::run()
{
    qDebug() << "QRunnable任务开始运行...";
    qDebug() << "当前线程ID:" << QThread::currentThreadId();
    
    if (m_notifier) {
        m_notifier->emitStarted();
        m_notifier->emitProgress("QRunnable任务:正在准备导出...");
    }
    
    try {
        if (m_notifier) {
            m_notifier->emitProgress("QRunnable任务:开始写入文件...");
        }
        
        bool success = Utilities::writeNumbersToFile(m_start, m_end, m_prefix);
        
        if (success) {
            QString message = QString("文件导出成功!范围:%1-%2").arg(m_start).arg(m_end);
            if (m_notifier) {
                m_notifier->emitProgress("QRunnable任务:导出完成");
                m_notifier->emitFinished(true, message);
            }
            qDebug() << "QRunnable任务完成:" << message;
        } else {
            QString message = "文件导出失败!";
            if (m_notifier) {
                m_notifier->emitFinished(false, message);
            }
            qDebug() << "QRunnable任务失败:" << message;
        }
        
    } catch (const std::exception& e) {
        QString message = QString("导出过程中发生异常: %1").arg(e.what());
        qDebug() << "QRunnable任务异常:" << message;
        if (m_notifier) {
            m_notifier->emitFinished(false, message);
        }
    }
    
    qDebug() << "QRunnable任务结束";
}

到此这篇关于Qt中实现多线程导出数据功能的四种方式小结的文章就介绍到这了,更多相关Qt多线程导出数据内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于Qt中实现多线程导出数据功能的四种方式小结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155616

相关文章

MySQL中DATE_FORMAT时间函数的使用小结

《MySQL中DATE_FORMAT时间函数的使用小结》本文主要介绍了MySQL中DATE_FORMAT时间函数的使用小结,用于格式化日期/时间字段,可提取年月、统计月份数据、精确到天,对大家的学习或... 目录前言DATE_FORMAT时间函数总结前言mysql可以使用DATE_FORMAT获取日期字段

基于Spring Boot 的小区人脸识别与出入记录管理系统功能

《基于SpringBoot的小区人脸识别与出入记录管理系统功能》文章介绍基于SpringBoot框架与百度AI人脸识别API的小区出入管理系统,实现自动识别、记录及查询功能,涵盖技术选型、数据模型... 目录系统功能概述技术栈选择核心依赖配置数据模型设计出入记录实体类出入记录查询表单出入记录 VO 类(用于

Go语言使用sync.Mutex实现资源加锁

《Go语言使用sync.Mutex实现资源加锁》数据共享是一把双刃剑,Go语言为我们提供了sync.Mutex,一种最基础也是最常用的加锁方式,用于保证在任意时刻只有一个goroutine能访问共享... 目录一、什么是 Mutex二、为什么需要加锁三、实战案例:并发安全的计数器1. 未加锁示例(存在竞态)

基于Redisson实现分布式系统下的接口限流

《基于Redisson实现分布式系统下的接口限流》在高并发场景下,接口限流是保障系统稳定性的重要手段,本文将介绍利用Redisson结合Redis实现分布式环境下的接口限流,具有一定的参考价值,感兴趣... 目录分布式限流的核心挑战基于 Redisson 的分布式限流设计思路实现步骤引入依赖定义限流注解实现

SpringBoot实现虚拟线程的方案

《SpringBoot实现虚拟线程的方案》Java19引入虚拟线程,本文就来介绍一下SpringBoot实现虚拟线程的方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录什么是虚拟线程虚拟线程和普通线程的区别SpringBoot使用虚拟线程配置@Async性能对比H

基于Python实现进阶版PDF合并/拆分工具

《基于Python实现进阶版PDF合并/拆分工具》在数字化时代,PDF文件已成为日常工作和学习中不可或缺的一部分,本文将详细介绍一款简单易用的PDF工具,帮助用户轻松完成PDF文件的合并与拆分操作... 目录工具概述环境准备界面说明合并PDF文件拆分PDF文件高级技巧常见问题完整源代码总结在数字化时代,PD

Linux之UDP和TCP报头管理方式

《Linux之UDP和TCP报头管理方式》文章系统讲解了传输层协议UDP与TCP的核心区别:UDP无连接、不可靠,适合实时传输(如视频),通过端口号标识应用;TCP有连接、可靠,通过确认应答、序号、窗... 目录一、关于端口号1.1 端口号的理解1.2 端口号范围的划分1.3 认识知名端口号1.4 一个进程

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

SpringBoot集成EasyExcel实现百万级别的数据导入导出实践指南

《SpringBoot集成EasyExcel实现百万级别的数据导入导出实践指南》本文将基于开源项目springboot-easyexcel-batch进行解析与扩展,手把手教大家如何在SpringBo... 目录项目结构概览核心依赖百万级导出实战场景核心代码效果百万级导入实战场景监听器和Service(核心

C# async await 异步编程实现机制详解

《C#asyncawait异步编程实现机制详解》async/await是C#5.0引入的语法糖,它基于**状态机(StateMachine)**模式实现,将异步方法转换为编译器生成的状态机类,本... 目录一、async/await 异步编程实现机制1.1 核心概念1.2 编译器转换过程1.3 关键组件解析