【问题标题】:Copying Files in Threads to prevent a freezed application在线程中复制文件以防止冻结的应用程序
【发布时间】:2016-06-27 07:07:37
【问题描述】:

我编写了一个带有“安装程序”功能的 C++/QT 应用程序。一切正常,但是当我在程序处于“复制过程”时在窗口外单击时,它会以某种方式失去焦点并冻结,直到复制过程结束,然后一切都以 QProgressBar 值为 100%。

我是这样复制的:

void Installer_C::copy(QString aSrcPath, QString aDstPath)
{    
  //handles
  hSrc = CreateFileW(..); //source file
  hDst = CreateFileW(..); //destination
  //copy
  ReadFile(...);
  LockFile(...);
  WriteFile(...); //->returnes bytesWritten
  UnlockFile(...);
  updateQProgressBar(bytesWritten); //updates progressbar in my application
  CloseHandle(...);
}

这个函数在一个 foreach 循环中被调用,该循环遍历带有文件的 QStringList(位于我的 launchInstall() 函数中)。
由于我的问题,我考虑为这个复制过程创建 Threads。是为每个Installer_C::copy() 调用创建一个新线程更有效,还是只创建一个线程来调用launchInstall() 函数(我认为它没有多大帮助)。
或者更好的问题:它甚至可以解决我的应用程序冻结的问题吗?我该怎么做才能让 ProgressBar 仍然从这个线程更新?

【问题讨论】:

    标签: c++ multithreading qt copy freeze


    【解决方案1】:

    我认为,解决您的问题的最佳方法是创建一个添加线程来复制进程。您可以使用 QThread(Qt 文档:QThread)类来创建一个线程,该线程将复制文件。主线程将执行您的 GUI,并且在文件复制期间可用。

    复制线程的小例子:

    class CopyThread : public QThread
    {
        Q_OBJECT
    private:
        QStringList oldfiles_;
        QStringList newfiles_;
    public:
        CopyThread(const QStringList& oldfiles,
                   const QStringList& newfiles,
                   QObject * parent = 0)
            : QThread(parent)
            , oldfiles_(oldfiles)
            , newfiles_(newfiles)
        {}
    
        void run() Q_DECL_OVERRIDE 
        {
            int min = qMin(oldfiles_.count(), newFiles.count());
            for(int i=0; i<min; ++i)
            {
                copyFile(oldfiles_.at(i), newFiles_.at(i));
                emit signalCopyFile(oldfiles_.at(i), newFiles_.at(i));
            }
        }
    signals:
        void signalCopyFile(const QString&, const QString&);
    private:
        void copyFile(QString aSrcPath, QString aDstPath)
        {
            QFile::copy(aSrcPath, aDstPath);
        }
    };
    

    当然,您必须在您的小部件上为signalCopyFile(const QString&amp;, const QString&amp;) 实现插槽并建立连接。一小段代码(例如)启动复制线程并建立连接:

    QStringList oldFiles;
    oldfiles.append("C:/1.txt");
    QStringList newFiles;
    newFiles.append("C:/2.txt");
    yourProgressBar.setMaximum(oldFiles.count());
    yourProgressBar.setMinimum(0);
    yourProgressBar.setValue(0);
    CopyThread *copyThread = new CopyThread(oldFiles, newFiles, this);
    connect(copyThread, &CopyThread::finished, copyThread, &QObject::deleteLater);
    connect(copyThread, &CopyThread::signalCopyFile, youWidget, &YouWidget::yourSlot);
    copyThread->start();
    

    yourSlot 中,您可以更新QProgressBar 的值:

    void yourSlot(const QString& oldFile, const QString& newFile)
    {
        // your actions when one file was copied
        yourProgressBar->setValue(yourProgressBar.value() + 1);
    }
    

    没有冻结一切都会好起来的!

    【讨论】:

    • 谢谢,那我直接打电话给CopyThread::run() 吗?对我来说,我发出的信号似乎很快,那么它们都在一个新线程中吗?我认为:信号发出>复制开始>下一个信号发出但复制尚未完成。你知道我的意思吗?
    • 我更新答案。每次复制一个文件时都会发出信号。
    【解决方案2】:

    据我所知,在 Qt 中解决这个问题有两种可能的方法:

    1. 使用QCoreApplication::processEvents()。正如文档所说,这不是一个糟糕的解决方案,适合处理长时间的操作。我认为它可能对你有用。例如,您可以在复制每个文件(或几个文件)后调用它。
    2. 使用多线程。这是划分 GUI 线程和逻辑线程的好方法。您可以根据自己的目标调整 Kirill 的 solution

    所以我的建议是:如果您需要简单快捷有效的解决方案,第一种方法适合您。

    如果您想制作设计良好且更复杂的应用程序,并且您准备编写更多代码,请使用第二种方法 .

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-14
      • 2015-06-27
      • 1970-01-01
      • 2021-09-05
      相关资源
      最近更新 更多