前言
在以往的项目开发中,在很多地方用到了多线程。针对不同的业务逻辑,需要使用不同的多线程实现方法,来达到优化项目的目的。本文记录下在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("文件导出成功!范围:%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任务结束";
}
到此这篇关于qt中实现多线程导出数据功能的四种方式小结的文章就介绍到这了,更多相关qt多线程导出数据内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论