Qt | 四种方式实现多线程导出数据功能

前言

在以往的项目开发中,在很多地方用到了多线程。针对不同的业务逻辑,需要使用不同的多线程实现方法,来达到优化项目的目的。本文记录下在Qt开发中用到的多线程技术实现方法,以导出指定范围的数字到txt文件为例,展示多线程不同的实现方式。
示例已上传到gittee,地址:https://gitee.com/zbylalalala1/qt_-thread-demo.git

Qt | 四种方式实现多线程导出数据功能

导出文件的示例工具类

首先提供一个工具类,用于将指定范围的数字写入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("文件导出成功!范围:%1-%2").arg(m_start).arg(m_end));         } 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::exportNumbersWithProgress() {     // 获取当前时间并格式化为文件名     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++;                  // 每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 exportFinished(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任务结束"; } 

发表评论

评论已关闭。

相关文章