Qt中實(shí)現(xiàn)多線程導(dǎo)出數(shù)據(jù)功能的四種方式小結(jié)
前言
在以往的項(xiàng)目開發(fā)中,在很多地方用到了多線程。針對(duì)不同的業(yè)務(wù)邏輯,需要使用不同的多線程實(shí)現(xiàn)方法,來(lái)達(dá)到優(yōu)化項(xiàng)目的目的。本文記錄下在Qt開發(fā)中用到的多線程技術(shù)實(shí)現(xiàn)方法,以導(dǎo)出指定范圍的數(shù)字到txt文件為例,展示多線程不同的實(shí)現(xiàn)方式。
示例已上傳到gittee,地址:https://gitee.com/zbylalalala1/qt_-thread-demo.git
導(dǎo)出文件的示例工具類
首先提供一個(gè)工具類,用于將指定范圍的數(shù)字寫入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() << "起始數(shù)字不能大于結(jié)束數(shù)字";
return false;
}
// 獲取當(dāng)前時(shí)間并格式化為文件名
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);
// 創(chuàng)建文件對(duì)象
QFile file(fileName);
// 以寫入模式打開文件
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "無(wú)法創(chuàng)建文件:" << fileName;
return false;
}
// 創(chuàng)建文本流
QTextStream out(&file);
// 寫入指定范圍的數(shù)字
int count = 0;
for (int i = start; i <= end; ++i) {
out << i;
count++;
// 每10個(gè)數(shù)字換行
if (count % 10 == 0 || i == end) {
out << "\n";
} else {
out << " "; // 數(shù)字之間用空格分隔
}
}
// 關(guān)閉文件
file.close();
qDebug() << "成功寫入文件:" << fileName;
qDebug() << "文件路徑:" << QDir::currentPath() + "/" + fileName;
qDebug() << "寫入數(shù)字范圍:" << start << "到" << end << ",共" << (end - start + 1) << "個(gè)數(shù)字";
return true;
}
// 獲取當(dāng)前工作目錄
static QString getCurrentPath()
{
return QDir::currentPath();
}
// 檢查文件是否存在
static bool fileExists(const QString& fileName)
{
QFile file(fileName);
return file.exists();
}
};
#endif // UTILITIES_H
QThread
使用QThread類來(lái)創(chuàng)建線程,是Qt中最簡(jiǎn)單的一種多線程實(shí)現(xiàn)方式,不過(guò)一般不建議使用,因?yàn)樗墓δ鼙容^有限。
使用QThread的方式為:繼承QThread并重寫run()函數(shù)。
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);
// 設(shè)置導(dǎo)出參數(shù)
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() << "導(dǎo)出線程開始運(yùn)行...";
emit exportStarted();
try {
bool success = Utilities::writeNumbersToFile(m_start, m_end, m_prefix);
if (success) {
emit exportFinished(true, QString("文件導(dǎo)出成功!范圍:%1-%2").arg(m_start).arg(m_end));
} else {
emit exportFinished(false, "文件導(dǎo)出失敗!");
}
qDebug() << "導(dǎo)出線程完成";
} catch (const std::exception& e) {
qDebug() << "導(dǎo)出過(guò)程中發(fā)生異常:" << e.what();
emit exportFinished(false, QString("導(dǎo)出過(guò)程中發(fā)生異常: %1").arg(e.what()));
}
}
使用方式:
ExportThread *exportThread = new ExportThread(this); exportThread->setExportParams(1, 10000, "numbers"); exportThread->start();
QObject的moveToThread方法實(shí)現(xiàn)多線程
QObject的moveToThread方法可以將一個(gè)QObject對(duì)象移動(dòng)到指定的線程中,實(shí)現(xiàn)多線程。
使用方式:
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() << "開始導(dǎo)出任務(wù)...";
m_shouldStop = false;
emit exportStarted();
emit statusUpdated("正在準(zhǔn)備導(dǎo)出...");
try {
bool success = false;
emit statusUpdated("使用自定義參數(shù)導(dǎo)出...");
success = exportNumbersWithProgress();
if (m_shouldStop) {
emit exportFinished(false, "導(dǎo)出已被用戶取消");
} else if (success) {
emit exportFinished(true, QString("文件導(dǎo)出成功!范圍:%1-%2").arg(m_start).arg(m_end));
} else {
emit exportFinished(false, "文件導(dǎo)出失??!");
}
} catch (const std::exception& e) {
qDebug() << "導(dǎo)出過(guò)程中發(fā)生異常:" << e.what();
emit exportFinished(false, QString("導(dǎo)出過(guò)程中發(fā)生異常: %1").arg(e.what()));
}
qDebug() << "導(dǎo)出任務(wù)完成";
}
void FileExportWorker::stopExport()
{
m_shouldStop = true;
emit statusUpdated("正在停止導(dǎo)出...");
}
bool FileExportWorker::exportNumbersWithProgress()
{
// 獲取當(dāng)前時(shí)間并格式化為文件名
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);
// 創(chuàng)建文件對(duì)象
QFile file(fileName);
// 以寫入模式打開文件
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "無(wú)法創(chuàng)建文件:" << fileName;
return false;
}
// 創(chuàng)建文本流
QTextStream out(&file);
int total = m_end - m_start + 1;
int count = 0;
// 寫入指定范圍的數(shù)字
for (int i = m_start; i <= m_end; ++i) {
if (m_shouldStop) {
file.close();
QFile::remove(fileName); // 刪除未完成的文件
return false;
}
out << i;
count++;
// 每10個(gè)數(shù)字換行
if (count % 10 == 0 || i == m_end) {
out << "\n";
} else {
out << " "; // 數(shù)字之間用空格分隔
}
// 每處理100個(gè)數(shù)字發(fā)送一次進(jìn)度更新
if (count % 100 == 0 || i == m_end) {
emit progressUpdated(count, total);
emit statusUpdated(QString("已處理 %1/%2 個(gè)數(shù)字").arg(count).arg(total));
// 讓出CPU時(shí)間,允許其他操作
QCoreApplication::processEvents();
}
}
// 關(guān)閉文件
file.close();
qDebug() << "成功寫入文件:" << fileName;
qDebug() << "文件路徑:" << QDir::currentPath() + "/" + fileName;
qDebug() << "寫入數(shù)字范圍:" << m_start << "到" << m_end << ",共" << total << "個(gè)數(shù)字";
return true;
}
QConcurrent實(shí)現(xiàn)多線程導(dǎo)出數(shù)據(jù)
QConcurrent是Qt提供的一個(gè)并發(fā)編程框架,用于簡(jiǎn)化多線程編程。它提供了一些方便的函數(shù)和類,用于在多個(gè)線程中執(zhí)行任務(wù)。本示例通過(guò)QConcurrent實(shí)現(xiàn)導(dǎo)出任務(wù),實(shí)現(xiàn)多線程導(dǎo)出數(shù)據(jù)。
使用方式:
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);
// 開始導(dǎo)出任務(wù)
void startExport(int start = 1, int end = 10000, const QString& prefix = "concurrent");
// 取消導(dǎo)出任務(wù)
void cancelExport();
// 檢查是否正在運(yùn)行
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的信號(hào)
connect(m_watcher, &QFutureWatcher<bool>::finished, this, &ConcurrentExporter::onExportFinished);
}
void ConcurrentExporter::startExport(int start, int end, const QString& prefix)
{
if (isRunning()) {
qDebug() << "導(dǎo)出任務(wù)已在運(yùn)行中";
return;
}
m_start = start;
m_end = end;
m_prefix = prefix;
qDebug() << "使用Qt Concurrent開始導(dǎo)出任務(wù)...";
qDebug() << "當(dāng)前線程ID:" << QThread::currentThreadId();
emit exportStarted();
m_future = QtConcurrent::run([=]() {
qDebug() << "工作線程ID:" << QThread::currentThreadId();
return Utilities::writeNumbersToFile(start, end, prefix);
});
// 設(shè)置QFutureWatcher監(jiān)視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 = "導(dǎo)出任務(wù)已被取消";
} else {
try {
success = m_future.result();
if (success) {
message = QString("文件導(dǎo)出成功!范圍:%1-%2").arg(m_start).arg(m_end);
} else {
message = "文件導(dǎo)出失??!";
}
} catch (const std::exception& e) {
message = QString("導(dǎo)出過(guò)程中發(fā)生異常: %1").arg(e.what());
}
}
emit exportFinished(success, message);
qDebug() << "Qt Concurrent導(dǎo)出任務(wù)完成:" << message;
}
QRunnable結(jié)合QThreadPool方法實(shí)現(xiàn)多線程導(dǎo)出數(shù)據(jù)
QRunnable是Qt提供的一個(gè)接口,用于在多線程中執(zhí)行任務(wù)。QThreadPool是一個(gè)線程池,用于管理多個(gè)線程。本示例通過(guò)QRunnable接口實(shí)現(xiàn)導(dǎo)出任務(wù),通過(guò)QThreadPool線程池管理線程,實(shí)現(xiàn)多線程導(dǎo)出數(shù)據(jù)。
使用方式:
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,我們需要一個(gè)信號(hào)發(fā)射器
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");
// 設(shè)置通知器,用于發(fā)送信號(hào)
void setNotifier(ExportTaskNotifier *notifier);
// 設(shè)置導(dǎo)出參數(shù)
void setExportParams(int start, int end, const QString& prefix);
// QRunnable接口實(shí)現(xiàn)
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)
{
// 設(shè)置任務(wù)完成后自動(dòng)刪除
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任務(wù)開始運(yùn)行...";
qDebug() << "當(dāng)前線程ID:" << QThread::currentThreadId();
if (m_notifier) {
m_notifier->emitStarted();
m_notifier->emitProgress("QRunnable任務(wù):正在準(zhǔn)備導(dǎo)出...");
}
try {
if (m_notifier) {
m_notifier->emitProgress("QRunnable任務(wù):開始寫入文件...");
}
bool success = Utilities::writeNumbersToFile(m_start, m_end, m_prefix);
if (success) {
QString message = QString("文件導(dǎo)出成功!范圍:%1-%2").arg(m_start).arg(m_end);
if (m_notifier) {
m_notifier->emitProgress("QRunnable任務(wù):導(dǎo)出完成");
m_notifier->emitFinished(true, message);
}
qDebug() << "QRunnable任務(wù)完成:" << message;
} else {
QString message = "文件導(dǎo)出失?。?;
if (m_notifier) {
m_notifier->emitFinished(false, message);
}
qDebug() << "QRunnable任務(wù)失敗:" << message;
}
} catch (const std::exception& e) {
QString message = QString("導(dǎo)出過(guò)程中發(fā)生異常: %1").arg(e.what());
qDebug() << "QRunnable任務(wù)異常:" << message;
if (m_notifier) {
m_notifier->emitFinished(false, message);
}
}
qDebug() << "QRunnable任務(wù)結(jié)束";
}
到此這篇關(guān)于Qt中實(shí)現(xiàn)多線程導(dǎo)出數(shù)據(jù)功能的四種方式小結(jié)的文章就介紹到這了,更多相關(guān)Qt多線程導(dǎo)出數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)單掌握桶排序算法及C++版的代碼實(shí)現(xiàn)
桶排序是將要排序的算法按桶分組排序之后再遍歷匯總的一種線性排序算法,下面就讓我們來(lái)通過(guò)小例子簡(jiǎn)單掌握桶排序算法及C++版的代碼實(shí)現(xiàn)^^2016-07-07
VC++實(shí)現(xiàn)View內(nèi)容保存為圖片的方法
這篇文章主要介紹了VC++實(shí)現(xiàn)View內(nèi)容保存為圖片的方法,涉及VC++中Bitmap類的save方法相關(guān)使用技巧,需要的朋友可以參考下2016-08-08
C語(yǔ)言內(nèi)存操作函數(shù)使用示例梳理講解
這篇文章主要介紹了C語(yǔ)言庫(kù)函數(shù)中的內(nèi)存操作函數(shù)memcpy()、memmove()、memset()、memcmp()使用示例分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09

