从std::元组函数QtConcurrentRun中获取(多个)返回值



嗨,我有一个在Qt中生成一些文件MD5的类,(我使用元组从中返回多个值),我想在其他线程上运行它,因为它可能生成所有文件MD5s需要一些时间和它的冻结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,但我不知道在哪里声明它是最好的地方(所以当函数超出范围时它不会删除)对不起,如果我不能解释我的问题正确,但我希望有人帮助我,谢谢

您可以通过信号发送它,而不是从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的成员。

注意:不需要传递3个int参数,可以发送std::tuple<int, int, int>。您必须为排队连接注册元类型,这是一种用于线程间连接的连接类型。

Q_DECLARE_METATYPE(std::tuple<int, int, int>);
qRegisterMetaType<std::tuple<int, int, int>>();

Edit1:QFutureWatcher<std::tuple<int,int,int>>替换为QFutureWatcher<void>,无需存储结果

Edit2:添加了当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)));

最新更新