嗨,我有一个用于在 Qt 中生成某个文件的 MD5 的类(我使用元组从中返回多个值),我想在其他线程上运行它,因为生成所有文件 MD5 可能需要一些时间,并且它的冻结gui
我决定使用 QtConcurrentRun 在其他线程上运行它,但到目前为止我对如何获取所有元组返回值没有任何想法
这是我的代码
HashGen.h
#pragma once
#include "stdafx.h"
class HashGen : public QObject
{
Q_OBJECT
public:
HashGen(QObject *parent = nullptr);
private:
QString Md5_gen(QString const& fname);
public slots:
std::tuple<int, int, int> check_sequential();
};
HashGen.cpp
#include "stdafx.h"
#include "HashGen.h"
HashGen::HashGen(QObject *parent)
: QObject(parent)
{
}
QString HashGen::Md5_gen(QString const& fname)
{
//Generate MD5 of giving file name
}
std::tuple<int, int, int> HashGen::check_sequential() {
QString file1[2] = { "8c0b1e6492078bdc113faae3d34fa5c5", "" }; // empty "" fill with other MD5 hash later
QString file2[4] = { "0547f42982dd9edb7b47931d00efff15", "", "", "" };
QString file3[2] = { "57f08e690e2655749291b2da4be8b021", "" };
QString file1_H = Md5_gen("/proj/file.zip");
QString file2_H = Md5_gen("/proj/file2.zip");
QString file3_H = Md5_gen("/proj/file3.zip");
int file1_status = 0;
int file2_status = 0;
int file3_status = 0;
for (int i = 0; i < 2; i++)
{
if (file1[i] != "nofile")
{
if (file1[i] == file1_H)
{
file1_status = i;
break;
}
else { file1_status = 422; } // Just a random number mean file doesn't match any MD5
}
else
{
file1_status = 404; // Just a random number mean file doesn't exist
break;
}
}
for (int i = 0; i < 4; i++)
{
if (file2[i] != "nofile")
{
if (file2[i] == file2_H)
{
file2_status = i;
break;
}
else { file2_status = 422; }
}
else
{
file2_status = 404;
break;
}
}
for (int i = 0; i < 2; i++)
{
if (file3[i] != "nofile")
{
if (file3[i] == file3_H)
{
file3_status = i;
break;
}
else { file3_status = 422; }
}
else
{
file3_status = 404;
break;
}
}
return { file1_status, file2_status, file3_status}; // Return all file status
mainwindow.cpp
void mainwindow::on_pushButton_clicked()
{
QFuture<std::tuple<int, int, int>> Hash = QtConcurrent::run(Gen, &HashGen::check_sequential);
QFutureWatcher<std::tuple<int, int, int>>* watcher = new QFutureWatcher<std::tuple<int, int, int>>;
connect(watcher, &QFutureWatcher<std::tuple<int, int, int>>::finished, this, &MafiaDEDLFox::AfterHash);
watcher->setFuture(Hash);
}
另一个问题是我需要使用 QFuturewatcher 来监视 QFuture,但我不知道在哪里声明它的最佳位置(因此当函数超出范围时它不会删除) 对不起,如果我不能正确解释我的问题,但我希望有人帮助我,谢谢
答案 0 :(得分:3)
您可以通过信号发送值,而不是从 QFutureWatcher
获取值。我们可以创建一个一次性的 QFutureWatcher
,它会在工作完成后自行删除。
class HashGen : public QObject
{
Q_OBJECT
public:
HashGen(QObject *parent = nullptr);
private:
QString Md5_gen(QString const& fname);
public slots:
std::tuple<int, int, int> check_sequential() {
...
// notify when completed
emit check_sequential_completed(file1_status, file2_status, file3_status);
return { file1_status, file2_status, file3_status}; // Return all file status
}
void async_check_sequential() {
// create future watcher as child
auto futureWatcher = new QFutureWatcher<void>(this);
// kill yourself when you done
connect(futureWatcher, &QFutureWatcher<void>::finished, futureWatcher, &QObject::deleteLater);
// Wait for finish of computation when HashGen about to die
connect(this, &QObject::destroyed, [futureWatcher](){ futureWatcher->waitForFinished(); });
// start check sequential in another thread
futureWatcher.setFuture(QtConcurrent::run(this, &HashGen::check_sequential));
}
signals:
void check_sequential_completed(int, int, int);
};
将 check_sequential_completed
连接到您需要的插槽。
connect(Gen, &HashGen::check_sequential_completed, this, &MafiaDEDLFox::AfterHash);
当然,您不必让未来的观察者成为一次性的。如果您发现每次都创建新对象效率低下,您可以将其保留为 HashGen
的成员。
注意:
可以发送 std::tuple<int, int, int>
,而不是传递 3 个 int 参数。您必须为排队连接注册元类型,这是一种用于线程间连接的连接类型。
Q_DECLARE_METATYPE(std::tuple<int, int, int>);
qRegisterMetaType<std::tuple<int, int, int>>();
编辑 1:
QFutureWatcher<std::tuple<int,int,int>>
替换为 QFutureWatcher<void>
,无需将结果存储在此解决方案中。
编辑 2:
添加的连接等待异步计算完成,当 HashGen
对象销毁时。所以线程不会继续在死对象上运行。但是当 HashGen
销毁时它会阻塞主线程一段时间。实际上这就是使用QFutureWatcher
的全部意义,否则如果您确定销毁HashGen
对象时不会有异步计算,则无需使用QFutureWatcher
。只需 QtConcurrent::run
就足够了。
仅使用 QtConcurrent::run 的示例
以下截取的代码来自实际工作项目。
class AddPrinterInvoker : public QObject
{
Q_OBJECT
public:
AddPrinterInvoker(QObject* parent = nullptr);
QStringList scanAddresses() {
...
emit addressListReady(addressList);
return addressList;
}
public slots:
void asyncScanAddresses() { QtConcurrent::run(this, &AddPrinterInvoker::scanAddresses); }
signals:
void addressListReady(QStringList addressList);
}
连接
connect(scanButton, SIGNAL(clicked()), m_invoker, SLOT(asyncScanAddresses()));
connect(m_invoker, SIGNAL(addressListReady(QStringList)), this, SLOT(updateAddressList(QStringList)));