如何停止对 QThread 的长时间操作?
How to stop long operation on QThread?
我进行了一些研究,但找不到我的解决方案为何不起作用的答案。但是说到点子上。
我在我的应用程序中将 QWidget 作为单独的对话框。我正在使用这个 QWidget class 来收集文件路径并使用 ZipLib 将它们压缩到一个文件中。为了向用户提供有关压缩进度的一些反馈,我添加了 QProgressBar,它会在压缩过程中更新。但我发现,在某些情况下,当文件太大时,压缩文件会花费很长时间,导致我的应用程序在此期间无法响应。所以我的想法是使用 QThread 将长时间的压缩操作移至另一个线程,一切正常,压缩工作正常,进度条已更新但是当我想取消压缩操作时出现问题。使用我当前的压缩方法操作只是不听我的任何中断线程的请求,即使在我关闭对话框后线程仍在进行压缩。我会告诉你我的方法,也许有一些非常微不足道的事情,但我就是无法取消这个压缩操作。
tl;博士版本:
我不能用 requestInterruption()
或任何其他方式中断 QThread。
这是我的 WorkerThread class:
class WorkerThread : public QThread
{
Q_OBJECT
public:
void setup(std::string zip, std::vector<std::string> file)
{
mZip = zip;
mFiles = file;
mActionStopped = false;
}
void run() override {
size_t progressValue = (100 / mFiles.size());
try
{
for (const auto& file : mFiles)
{
if (not isInterruptionRequested())
{
Q_EMIT updateTheProgress(progressValue);
ZipFile::AddFile(mZip, file);
}
else
{
return;
}
}
}
catch(const std::exception& e)
{
Q_EMIT resultReady(false);
}
if (not mActionStopped)
{
Q_EMIT updateTheProgress(100 - actualProgress); // To set 100%
Q_EMIT resultReady(true);
}
}
std::string mZip;
std::vector<std::string> mFiles;
size_t actualProgress = 0;
bool mActionStopped;
Q_SIGNALS:
void resultReady(bool dupa);
void updateProgress(int value);
public Q_SLOTS:
void updateTheProgress(int value)
{
actualProgress += value;
Q_EMIT updateProgress(actualProgress);
}
void stopWork()
{
mActionStopped = true;
}
};
在我的 QWidget 中 class 我有这样的东西:
workerThread = new WorkerThread();
connect(workerThread, &WorkerThread::resultReady, this, &ZipProjectDialog::zipDone);
connect(workerThread, SIGNAL(updateProgress(int)), progressBar, SLOT(setValue(int)));
connect(btnBox, &QDialogButtonBox::rejected, workerThread, &WorkerThread::requestInterruption);
connect(btnBox, &QDialogButtonBox::rejected, workerThread, &WorkerThread::stopWork);
workerThread->setup(zipFilename, filePathList);
workerThread->start();
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
我已按照 QThread 文档进行操作,但 requestInterruption()
仍然无法正常工作。
如果有人知道如何解决这个问题,我将不胜感激。
谢谢!
您不能中断
的执行
ZipFile::AddFile(mZip, file);
本身。这是对函数的一次调用,没有人检查该函数内部的 isInterruptionRequested()
。
一个简化的例子
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
class MyThread : public QThread
{
public:
void run() override
{
qDebug() << "starting";
for (int i = 0; i < 10; ++i) {
qDebug() << "loop" << i;
if (!isInterruptionRequested())
sleep(5);
else
break;
}
qDebug() << "finished";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread;
QTimer::singleShot(0, &thread, [&thread] {
thread.start();
QTimer::singleShot(7000 /*7 seconds*/, &thread, [&thread] {
qDebug() << "interrupting";
thread.requestInterruption();
});
});
return a.exec();
}
打印
starting
loop 0
loop 1
interrupting
loop 2
finished
符合预期。正如预期的那样,“中断”和“循环 2”之间有几秒钟的延迟,“循环 2”和“完成”之间没有延迟。
如果你想更细粒度地中断你的任务,那么你必须找到比 ZipFile::AddFile 更细粒度的 API,这样你就可以定期检查 isInterruptionRequested()
即使是单个文件已存档,或使用可以终止的外部进程。
我进行了一些研究,但找不到我的解决方案为何不起作用的答案。但是说到点子上。
我在我的应用程序中将 QWidget 作为单独的对话框。我正在使用这个 QWidget class 来收集文件路径并使用 ZipLib 将它们压缩到一个文件中。为了向用户提供有关压缩进度的一些反馈,我添加了 QProgressBar,它会在压缩过程中更新。但我发现,在某些情况下,当文件太大时,压缩文件会花费很长时间,导致我的应用程序在此期间无法响应。所以我的想法是使用 QThread 将长时间的压缩操作移至另一个线程,一切正常,压缩工作正常,进度条已更新但是当我想取消压缩操作时出现问题。使用我当前的压缩方法操作只是不听我的任何中断线程的请求,即使在我关闭对话框后线程仍在进行压缩。我会告诉你我的方法,也许有一些非常微不足道的事情,但我就是无法取消这个压缩操作。
tl;博士版本:
我不能用 requestInterruption()
或任何其他方式中断 QThread。
这是我的 WorkerThread class:
class WorkerThread : public QThread
{
Q_OBJECT
public:
void setup(std::string zip, std::vector<std::string> file)
{
mZip = zip;
mFiles = file;
mActionStopped = false;
}
void run() override {
size_t progressValue = (100 / mFiles.size());
try
{
for (const auto& file : mFiles)
{
if (not isInterruptionRequested())
{
Q_EMIT updateTheProgress(progressValue);
ZipFile::AddFile(mZip, file);
}
else
{
return;
}
}
}
catch(const std::exception& e)
{
Q_EMIT resultReady(false);
}
if (not mActionStopped)
{
Q_EMIT updateTheProgress(100 - actualProgress); // To set 100%
Q_EMIT resultReady(true);
}
}
std::string mZip;
std::vector<std::string> mFiles;
size_t actualProgress = 0;
bool mActionStopped;
Q_SIGNALS:
void resultReady(bool dupa);
void updateProgress(int value);
public Q_SLOTS:
void updateTheProgress(int value)
{
actualProgress += value;
Q_EMIT updateProgress(actualProgress);
}
void stopWork()
{
mActionStopped = true;
}
};
在我的 QWidget 中 class 我有这样的东西:
workerThread = new WorkerThread();
connect(workerThread, &WorkerThread::resultReady, this, &ZipProjectDialog::zipDone);
connect(workerThread, SIGNAL(updateProgress(int)), progressBar, SLOT(setValue(int)));
connect(btnBox, &QDialogButtonBox::rejected, workerThread, &WorkerThread::requestInterruption);
connect(btnBox, &QDialogButtonBox::rejected, workerThread, &WorkerThread::stopWork);
workerThread->setup(zipFilename, filePathList);
workerThread->start();
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
我已按照 QThread 文档进行操作,但 requestInterruption()
仍然无法正常工作。
如果有人知道如何解决这个问题,我将不胜感激。
谢谢!
您不能中断
的执行ZipFile::AddFile(mZip, file);
本身。这是对函数的一次调用,没有人检查该函数内部的 isInterruptionRequested()
。
一个简化的例子
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
class MyThread : public QThread
{
public:
void run() override
{
qDebug() << "starting";
for (int i = 0; i < 10; ++i) {
qDebug() << "loop" << i;
if (!isInterruptionRequested())
sleep(5);
else
break;
}
qDebug() << "finished";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread;
QTimer::singleShot(0, &thread, [&thread] {
thread.start();
QTimer::singleShot(7000 /*7 seconds*/, &thread, [&thread] {
qDebug() << "interrupting";
thread.requestInterruption();
});
});
return a.exec();
}
打印
starting
loop 0
loop 1
interrupting
loop 2
finished
符合预期。正如预期的那样,“中断”和“循环 2”之间有几秒钟的延迟,“循环 2”和“完成”之间没有延迟。
如果你想更细粒度地中断你的任务,那么你必须找到比 ZipFile::AddFile 更细粒度的 API,这样你就可以定期检查 isInterruptionRequested()
即使是单个文件已存档,或使用可以终止的外部进程。