multithreading - 使用带有GUI的QT复制文件的正确方法,可避免卡住

标签 multithreading qt user-interface freeze qfile

我有一个应用程序,使用QFile::Copy(..)将文件从一个位置复制到另一个位置。复制过程是在单独的线程中执行到工作对象中的,但是guit_strong有时会冻结GUI的,我在这里阅读了很多与此相关的主题,但是我知道这种方法( worker 类)是正确的。我在其他项目上使用相同的方法在另一个线程(如天文钟表)中执行过程,它的工作原理非常好,流畅。似乎只有在复制文件时才会发生这种冻结。在MS Windows中,“延迟”比在Linux中更明显,在最后一个中,除非复制了一个大文件(700MB),否则不会检测到冻结,只有在文件结束复制时,才在复制过程中显示GUI react 灵敏。我在mainWindow (BUMain)类中使用了这种方法:

void BUMain::initThreadSetup()
{
    thread = new QThread;
    Worker *worker = new Worker();
    worker->moveToThread(thread);

    connect(worker,SIGNAL(worker_Signal_updateProgressBar(int)),ui->progressBar,SLOT(setValue(int)),Qt::QueuedConnection);
    connect(this,SIGNAL(main_signal_copyFile(int,QStringList,QString)),worker,SLOT(worker_Slot_copyFile(int,QStringList,QString)),Qt::QueuedConnection);
    connect(worker,SIGNAL(worker_signal_keepCopying()),this,SLOT(main_slot_keepCopying()),Qt::QueuedConnection);
    connect(worker,SIGNAL(worker_signal_logInfo(QString)),gobLogViewer,SLOT(logger_slot_logInfo(QString)),Qt::QueuedConnection);

    connect(thread,SIGNAL(finished()),worker,SLOT(deleteLater()));
    connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()));
    thread->start();

}

在MainGui(BUMain)构造函数中调用此方法以启动线程。一些相关的代码:

这是发出worker_signal_keepCopying()时调用的插槽:
void BUMain::main_slot_keepCopying()
{
    giProgress ++;
    if(giProgress < giFileCounter){
        emit(main_signal_copyFile(giProgress,gobPaths,ui->toFilesTextField->text()));
    }
}

在这里,我进行了计数器验证,并发出一个新信号来通知工作人员它可以继续下一个副本。复制过程是一个文件一个地完成的。在worker.cpp文件中,您可以看到worker_Slot_copyFile(int liIndex,QStringList files,QString path)插槽的实现:

worker.cpp:
#include "worker.h"
#include <QFile>
#include <QFileInfo>
#include <QStringList>
#include <QCoreApplication>

Worker::Worker(QObject *parent) :
    QObject(parent)
{

}

void Worker::worker_Slot_copyFile(int liIndex,QStringList files,QString path)
{
    QString fileName;

    fileName = QFileInfo(files.at(liIndex)).baseName()+"."+QFileInfo(files.at(liIndex)).completeSuffix();

    //If the file exist, delete it
    if (QFile::exists(path+"/"+fileName))
    {
        QFile::remove(path+"/"+fileName);
    }

    QFile lobFile(files.at(liIndex));

    if(lobFile.copy(path+"/"+fileName)){

        //Write to a logger class
        emit(worker_signal_logInfo("File: " + fileName + " copied to: " + path));

        //Update a progress bar in the main GUI
        emit(worker_Signal_updateProgressBar(liIndex+1));
    }

    //The file has been processed!, I'm ready to copy another file...
    emit(worker_signal_keepCopying());

}

worker.h:
#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QThread>
#include <QStringList>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = 0);

signals:
    void worker_Signal_updateProgressBar(int value);
    void worker_signal_keepCopying();
    void worker_signal_logInfo(QString info);

public slots:
    void worker_Slot_copyFile(int liIndex, QStringList files, QString path);

};

#endif // WORKER_H

因此,换句话说,过程可能是:

好的!让我们开始复制一些文件!!记住BUMain是运行GUI的mainWindow类:
  • BUMain将进度条的最大值调整为要复制
  • 的文件总数
  • BUMain将进度条的值设置为0
  • BUMain发出main_signal_copyFile(...)信号。这就像“嘿, worker !请开始复制一个新文件,我将在这里做另一件事,当您完成复制后,请告诉我。”
  • Worker接收信号并调用worker_Slot_copyFile。就像“嘿BUMain,我能听到您的声音,收到订单!现在我将复制文件”
  • Worker完成发出worker_signal_keepCopying()的副本:“BUMain我已经结束了文件的复制,如果需要,我可以复制另一个文件,只是通知我。”
  • BUMain调用main_slot_keepCopying():“我能听到你的声音,谢谢!”再次发出main_signal_copyFile(...):“工作人员,我有更多文件要复制,请复制另一个文件”。
  • 重复该过程,直到处理完所有文件。

  • 有时,该过程确实运行良好,没有滞后或冻结,但有时却没有。请注意,此方法旨在不阻塞。

    我也尝试过在worker类中运行for循环以复制所有文件而不通知主类,但是延迟很大,并且在复制大文件(> 300MB)时GUI变得无响应。例如:
    void Worker::worker_Slot_copyFile(int liIndex,QStringList files,QString path)
    {
        QString fileName;
        for(int liIndex = 0; liIndex < files.length() - 1; liIndex ++){
            fileName = QFileInfo(files.at(liIndex)).baseName()+"."+QFileInfo(files.at(liIndex)).completeSuffix();
    
            if (QFile::exists(path+"/"+fileName))
            {
                QFile::remove(path+"/"+fileName);
            }
    
            QFile lobFile(files.at(liIndex));
    
            if(lobFile.copy(path+"/"+fileName)){
                emit(worker_signal_logInfo("File: " + fileName + " copied to: " + path));
                emit(worker_Signal_updateProgressBar(liIndex+1));
            }
        }
    }
    

    我希望尽可能清楚,这很难解释。我已将this用作使用 worker 类(Class)方法的引用。

    注意:我正在使用QT5.1.1进行编程和Windows 10,并使用Arch Linux来部署该应用程序。

    任何帮助或建议,不胜感激。在此先感谢您,祝您度过愉快的一天!

    最佳答案

    如果有点冗长,您的方法是正确的。之所以会出现“滞后”,是因为内核对如何管理页面缓存以及清除应用程序的各个部分以为要复制的文件中的页面腾出空间感到愚蠢。唯一的解决方法可能是使用特定于平台的文件复制机制,该机制建议内核将按顺序读取和写入,并且在完成写入后不再需要数据。有关详细信息和一些Linux代码,请参见this answer。在Windows上,希望CopyFileEx进行正确的咨询调用Qt example is here

    关于multithreading - 使用带有GUI的QT复制文件的正确方法,可避免卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38746524/

    相关文章:

    java - 如何向现有 Java Swing 程序添加登录屏幕?

    delphi - 在 OpenGL 渲染上下文之上叠加图形

    c# - 为什么 MemoryCache 抛出 NullReferenceException

    c++ - 使用不同的参数函数运行 std::thread

    multithreading - 简单的多线程Haskell占用大量内存

    javascript - 用Qt制作一个简单的LED指示灯

    c++ - 为 win32 线程使用不同的静态库内存

    c++ - 仪器一直告诉我我有内存泄漏

    c++ - Qt Regex 帮助(数组键)

    c++ - 关于 MVC View 界面的良好设计实践