尝试获取文件的MD5时Qt程序冻结

问题描述

您好,我使用此代码在QT中生成文件的MD5

QString Md5_gen(QString const &s)
{
    QString pakchunk_Md5;
    QCryptographicHash crypto(QCryptographicHash::Md5);
    QFile pakchunk("D:/Games/TDPA - Man of Medan" + s);
    if (pakchunk.open(qiodevice::ReadOnly))
    {
        while(!pakchunk.atEnd()){
        crypto.addData(pakchunk.read(8192));
        }
    } else
    {
        qDebug() << "Can't open file.";
        pakchunk_Md5 = "nofile";
        return pakchunk_Md5;
    }
    pakchunk_Md5 = crypto.result().toHex();
    return pakchunk_Md5;
}

我需要使用此代码(1.5GB>)生成8个大文件的MD5,问题是当我按下按钮以启动Generate MD5时,GUI 冻结,直到所有MD5生成
我通过这种方式测试 QFuture QFutureWatcher QtConcurrent ,但是没有运气的GUi每次都能保持不变

main.cpp

#include "user_def.h"
#include "mainwindow2.h"
#include...

QString Md5_gen(QString const &s)
{
    QString pakchunk_Md5;
    QCryptographicHash crypto(QCryptographicHash::Md5);
    QFile pakchunk("D:/Games/TDPA - Man of Medan" + s);
    if (pakchunk.open(qiodevice::ReadOnly))
    {
        while(!pakchunk.atEnd()){
        crypto.addData(pakchunk.read(8192));
        }
    } else
    {
        qDebug() << "Can't open file.";
        pakchunk_Md5 = "nofile";
        return pakchunk_Md5;
    }
    pakchunk_Md5 = crypto.result().toHex();
    return pakchunk_Md5;
}

int main(int argc,char *argv[]) {
  QApplication a(argc,argv);

  a.setStyle(new DarkStyle);

  FramelessWindow framelessWindow;
  framelessWindow.setwindowIcon(a.style()->standardIcon(qstyle::SP_DesktopIcon));

  MainWindow *mainWindow = new MainWindow;

  framelessWindow.setContent(mainWindow);
  framelessWindow.show();

  return a.exec();
}


user_def.h

#ifndef USER_DEF_H
#define USER_DEF_H
#include <QString>

QString Md5_gen(QString const &s);

#endif // USER_DEF_H


mainwindow2.h

#ifndef MAINWINDOW2_H
#define MAINWINDOW2_H

#include <QMainWindow>
#include <QtConcurrentRun>
#include <QFuture>
#include <QFutureWatcher>
#include <QThread>
#include <QThreadPool>
#include "user_def.h"

namespace Ui {
class MainWindow2;
}

class MainWindow2 : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow2(QWidget *parent = nullptr);
    ~MainWindow2();

public slots:
      void run_thread();
      void displayFinishedBox();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow2 *ui;
    QFutureWatcher<QString> *watcher;
    QFuture<QString> *future;
};

#endif // MAINWINDOW2_H


mainwindow2.cpp

#include...

MainWindow2::MainWindow2(QWidget *parent) :
    QMainWindow(parent),ui(new Ui::MainWindow2)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow2::run_thread);


    // display a message Box when the calculation has finished

    future = new QFuture<QString>;
    watcher = new QFutureWatcher<QString>;

    connect(watcher,SIGNAL(finished()),SLOT(displayFinishedBox()));

}

MainWindow2::~MainWindow2()
{
    delete ui;
}

void MainWindow2::run_thread()
{
    int file_ok = 0;
    //file pak33
    QString loc33 = "/SM/test1.pak";
    QFuture<QString> future33 = QtConcurrent::run(QThreadPool::globalInstance(),Md5_gen,loc33);
    watcher->setFuture(future33);
    QString pakchunk33 = future33.result();

    qDebug() << pakchunk33;
    if (pakchunk33 == "f7002d4419cd235a87746715ba6fb2ea")
    {
        qDebug() << "OK";
        file_ok++;
        ui->label_8->setText("OK");
        ui->label_8->setStyleSheet("QLabel { color : green; }");
    } else if (pakchunk33 == "nofile")
    {
        qDebug() << "no file found";
        ui->label_8->setText("not found");
        ui->label_8->setStyleSheet("QLabel { color : red; }");
    } else
    {
        qDebug() << "file is diffrent";
        ui->label_8->setText("wrong");
        ui->label_8->setStyleSheet("QLabel { color : red; }");
    }
    ui->progressBar->setValue(12);

    //file pak34
    QString loc34 = "/SM/test2.pak";
    QFuture<QString> future34 = QtConcurrent::run(QThreadPool::globalInstance(),loc34);
    watcher->setFuture(future34);
    QString pakchunk34 = future34.result();
    qDebug() << pakchunk34;

    if (pakchunk34 == "64c77598586b6c3cb60beed0b0750620")
    {
        qDebug() << "OK";
        file_ok++;
        ui->label->setText("OK");
        ui->label->setStyleSheet("QLabel { color : green; }");
    } else if (pakchunk34 == "nofile")
    {
        qDebug() << "no file found";
        ui->label->setText("not found");
        ui->label->setStyleSheet("QLabel { color : red; }");
    } else
    {
        qDebug() << "file is diffrent";
        ui->label->setText("wrong");
        ui->label->setStyleSheet("QLabel { color : red; }");
    }
    ui->progressBar->setValue(25);

    //file pak35
    QString loc35 = "/SM/test3.pak";
    QFuture<QString> future35 = QtConcurrent::run(QThreadPool::globalInstance(),loc35);
    watcher->setFuture(future35);
    QString pakchunk35 = future35.result();

    qDebug() << pakchunk35;
    if (pakchunk35 == "ee53f7a7656a32b5278c460baec46f16")
    {
        qDebug() << "OK";
        file_ok++;
        ui->label_7->setText("OK");
        ui->label_7->setStyleSheet("QLabel { color : green; }");
    } else if (pakchunk35 == "nofile")
    {
        qDebug() << "no file found";
        ui->label_7->setText("not found");
        ui->label_7->setStyleSheet("QLabel { color : red; }");
    } else
    {
        qDebug() << "file is diffrent";
        ui->label_7->setText("wrong");
        ui->label_7->setStyleSheet("QLabel { color : red; }");
    }
    ui->progressBar->setValue(38);

    /*Some other code*/

谁能说我的问题是什么,我该如何解决

修改1

我以这种方式编辑代码 它工作良好,没有冻结GUI 就编码而言,这是标准的吗?

mainwindow2.cpp

MainWindow2::MainWindow2(QWidget *parent) :
    QMainWindow(parent),ui(new Ui::MainWindow2)
{
    ui->setupUi(this);
    connect(ui->pushButton_3,&MainWindow2::MD5_thread_1);

    future = new QFuture<QString>;
    watcher1 = new QFutureWatcher<QString>;
    watcher2 = new QFutureWatcher<QString>;
    watcher3 = new QFutureWatcher<QString>;

    connect(watcher1,&QFutureWatcher<QString>::finished,&MainWindow2::MD5_thread_2);
    connect(watcher2,&MainWindow2::MD5_thread_3);
    connect(watcher3,&MainWindow2::MD5_thread_4);
    //some other code
}

void MainWindow2::MD5_thread_1()
{
    ui->pushButton->setEnabled(false);
    ui->pushButton->setText("procces started");
    ui->pushButton->setStyleSheet("QPushButton { color : white; background-color: rgb(73,80,93); }");
    ui->label->setText("checking");
    ui->label_2->setText("checking");
    ui->label_3->setText("checking");
    ui->label_4->setText("checking");
    ui->label_5->setText("checking");
    ui->label_6->setText("checking");
    ui->label_7->setText("checking");
    ui->label_8->setText("checking");
    ui->label_14->setText("waiting for end of check");
    ui->progressBar->setRange(0,100);
    ui->progressBar_2->setRange(0,100);
    ui->progressBar->setValue(0);
    ui->progressBar_2->setValue(0);

    //file pak33
    QString loc33 = "/SMG0/editor.pak";
    *future= QtConcurrent::run(QThreadPool::globalInstance(),loc33);
    watcher1->setFuture(*future);
}

void MainWindow2::MD5_thread_2()
{
    QString pakchunk33 = future->result();

    qDebug() << pakchunk33;
    if (pakchunk33 == "f7002d4419cd235a87746715ba6fb2ea")
    {
        qDebug() << "OK";
        file_ok++;
        ui->label_8->setText("OK");
        ui->label_8->setStyleSheet("QLabel { color : green; }");
    } else if (pakchunk33 == "nofile")
    {
        qDebug() << "no file found";
        ui->label_8->setText("not found");
        ui->label_8->setStyleSheet("QLabel { color : red; }");
    } else
    {
        qDebug() << "file is diffrent";
        ui->label_8->setText("wrong");
        ui->label_8->setStyleSheet("QLabel { color : red; }");
    }
    ui->progressBar->setValue(12);
    watcher1->deleteLater();

    //file pak34
    QString loc34 = "/SMG0/2Editor.pak";
    *future = QtConcurrent::run(QThreadPool::globalInstance(),loc34);
    watcher2->setFuture(*future);
}

void MainWindow2::MD5_thread_3()
{
    QString pakchunk34 = future->result();
    qDebug() << pakchunk34;

    if (pakchunk34 == "64c77598586b6c3cb60beed0b0750620")
    {
        qDebug() << "OK";
        file_ok++;
        ui->label->setText("OK");
        ui->label->setStyleSheet("QLabel { color : green; }");
    } else if (pakchunk34 == "nofile")
    {
        qDebug() << "no file found";
        ui->label->setText("not found");
        ui->label->setStyleSheet("QLabel { color : red; }");
    } else
    {
        qDebug() << "file is diffrent";
        ui->label->setText("wrong");
        ui->label->setStyleSheet("QLabel { color : red; }");
    }
    ui->progressBar->setValue(25);
    watcher2->deleteLater();

    //file pak35
    QString loc35 = "/SMG0/3Editor.pak";
    *future = QtConcurrent::run(QThreadPool::globalInstance(),loc35);
    watcher3->setFuture(*future);
}


void MainWindow2::core_install
{
    QString pakchunk35 = future->result();

    qDebug() << pakchunk40;
    if (pakchunk40 == "49e0440340044f424caeb82bade1301f")
    {
        qDebug() << "OK";
        file_ok++;
        ui->label_2->setText("OK");
        ui->label_2->setStyleSheet("QLabel { color : green; }");
    } else if (pakchunk40 == "nofile")
    {
        qDebug() << "no file found";
        ui->label_2->setText("not found");
        ui->label_2->setStyleSheet("QLabel { color : red; }");
    } else
    {
        qDebug() << "file is diffrent";
        ui->label_2->setText("wrong");
        ui->label_2->setStyleSheet("QLabel { color : red; }");
    }
    ui->progressBar->setValue(100);
    watcher3->deleteLater();

    //check if game is okey or not
    if (file_ok == 8)
    {
        ui->label_14->setText("O");
        ui->label_14->setStyleSheet("QLabel { color : green; }");
    } else
    {
        ui->label_14->setText("X");
        ui->label_14->setStyleSheet("QLabel { color : red; }");
    }
}

mainwindow2.h

#ifndef MAINWINDOW2_H
#define MAINWINDOW2_H

#include <QMainWindow>
#include <QtConcurrentRun>
#include <QFuture>
#include <QFutureWatcher>
#include <QThread>
#include <QThreadPool>
#include "user_def.h"

namespace Ui {
class MainWindow2;
}

class MainWindow2 : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow2(QWidget *parent = nullptr);
    ~MainWindow2();

public slots:
      void MD5_thread_1();
      void MD5_thread_2();
      void MD5_thread_3();
      void core_install();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

    void on_radioButton_2_clicked();

    void on_radioButton_4_clicked();

    void on_radioButton_3_clicked();

    void on_radioButton_clicked();

private:
    Ui::MainWindow2 *ui;
    QFutureWatcher<QString> *watcher1;
    QFutureWatcher<QString> *watcher2;
    QFutureWatcher<QString> *watcher3;
    QFuture<QString> *future;
    int file_ok = 0;
};

#endif // MAINWINDOW2_H

解决方法

result方法会阻塞您的UI线程,从而使整个并发/将来的舞会变得毫无用处。

将来创建一个监视程序,并将其finished信号链接到一个lambda,该lambda将文件名和结果的内容都传递到窗口上的onHashCalculated方法。通过这种方法,您可以检查哈希值是否与您预定义的哈希值之一匹配,并更新UI。

或者,将当前的run_thread函数移动到在单独的线程上运行的单独的QObject上,并使其发出hashCalculated(name,hash)信号,表示您的主窗口使用onHashCalculated插槽来订阅到我上面描述的那个。

这是封装两种方法的HashChecker的代码。

请注意将QMap链接到doneFile的方式的静态checkDone映射文件名到哈希。

enum class Status { Ok,NotOk,Missing };

class HashChecker : public QObject {
    Q_OBJECT

public:
    QMap<QString,Status> done;

    HashChecker(QObject *parent = nullptr) : QObject(parent) {
        QObject::connect(this,&HashChecker::doneFile,this,&HashChecker::checkDone);
    }

    inline static const QMap<QString,QString> hashes = {
        {"/SM/test1.pak","f7002d4419cd235a87746715ba6fb2ea"},{"/SM/test2.pak","64c77598586b6c3cb60beed0b0750620"},{"/SM/test3.pak","ee53f7a7656a32b5278c460baec46f16"},};

signals:
    void finished();
    void doneFile(const QString& fname,Status s);

private slots:
    void checkDone(const QString& fname,Status s) {
        done[fname] = s;
        if (done.size() == hashes.size())
            emit finished();
    }

public slots:
    void check_parallel() {
        for (auto it = hashes.cbegin(); it != hashes.cend(); it++) {
            auto fname = it.key();
            auto hash = it.value();

            QFuture<Status> fut = QtConcurrent::run(do_hash,fname,hash);
            QFutureWatcher<Status> *fw = new QFutureWatcher<Status>(this);
            fw->setFuture(fut);
            QObject::connect(fw,&QFutureWatcher<Status>::finished,[=]() {
                    fw->deleteLater();
                    emit doneFile(fname,fut.result());
            });
        }
    }

    void check_sequential() {
        for (auto it = hashes.cbegin(); it != hashes.cend(); it++) {
            auto fname = it.key();
            auto hash = it.value();

            auto result = do_hash(fname,hash);
            emit doneFile(fname,result);
        }
    }
};

如果要并行检查文件:

HashChecker *hc = new HashChecker();
QObject::connect(hc,&MainWindow2::onHashCalculated);
hc->check_parallel();

不同线程中的顺序几乎相同:

QThread *t = new QThread(this);
HashChecker *hc = new HashChecker();
hc->moveToThread(t);
QObject::connect(t,&QThread::started,hc,&HashChecker::check_sequential);
QObject::connect(hc,&MainWindow2::onHashCalculated);
QObject::connect(hc,&HashChecker::finished,t,&QThread::quit);
t->start();