7

我正在尝试杀死/取消 QThread。我遵循了https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/的实施

我像这样创建 QThread:

workerThread = new QThread();
ImageProcessor* worker = new ImageProcessor();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
workerThread->start();

并尝试通过调用在某个 UI 事件上停止它:

workerThread->quit();

我的工人看起来像这样:

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

signals:
void finished();

public slots:
void process();

private:
//...
};

进程调用具有大量计算的 API

void ImageProcessor::process()
{
// API call...

emit finished();
}

我的问题是,在线程上调用退出后,线程继续运行,即 API 的计算不会停止。因为工作人员只调用 API,所以我无法在工作人员循环中使用取消检查。有任何想法吗?

提前致谢!

4

5 回答 5

2

您的繁重任务是如此简单,以至于我会使用QtConcurrent::run并忘记 QThread。

ImageProcessor* worker = new ImageProcessor();
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
QtConcurrent::run(worker, &ImageProcessor::process);

这正是您所需要的,而且速度会更快一些,因为它将使用线程池中的线程,而不是创建一个新线程。

只要记住使用Qt::AutoConnection(默认值)。

要优雅地停止处理,您必须在您的 process 方法中对一些变量进行极化:

bool ImageProcessor::needEndNow() {
    QMutexLocker locker(mutex);
    return endNowRequested;
}

void ImageProcessor::requestEndNow() {
    QMutexLocker locker(mutex);
    endNowRequested = true;
}

void ImageProcessor::process()
{
   while(!needEndNow()) {
       // nextStep of processing
   }

   emit finished();
}
于 2013-08-07T12:21:35.910 回答
2

如果你有这样的东西,没有任何循环:

void ImageProcessor::process()
{
    worker_api(); // that takes a lot of time
    emit finished();
}

QThread 几乎无能为力。你可以做到,从好到坏:

  • 也许您正在使用的 API 有另一个调用来停止该工作调用。但是既然你在这里问,我想没有这样的事情。
  • 在不再需要时使用QProcess和它从不同的进程启动工作人员调用。kill优点:不用担心清理。对比:检索结果可能很困难。
  • QThread::terminate,但不建议使用(请参阅文档中的警告:基本上,没有清理)。
  • 在 Windows 中,QThread::currentThreadId为您提供一个可以用于其他操作系统调用的编号(请参阅文档中的警告)。
于 2014-05-14T10:14:10.403 回答
1

您不能停止已阻塞的 QThread,至少不能以任何安全的方式停止。

例如,如果你输入了这个函数

void neverStop() {
    int *arr = new int[1024*1024];
    for (;;) {
    }
}

线程不会响应任何退出消息,虽然大多数操作系统都有强制终止线程的方法,但任何分配的内存都不会被正确释放。

于 2013-01-06T15:41:05.370 回答
1

即API 的计算不会停止。

所以你试图在计算过程中停止它?

QThread::quit 仅在计算完所有内容后才停止它。

您可以将计算拆分为较小的任务,甚至可以添加取消标志。在 ImageProcessor 对象上有一个普通的方法来设置它:

class ImageProcessor{
   void stop(){
     canceled = true;
   }
private:
   bool canceled;
}

然后检查是否(取消)返回;在进程槽中。(只写布尔是一种线程安全的)

然后调用 stop(); 在对象上并在线程上退出()。

于 2013-01-06T11:55:41.120 回答
0

而不是使用thread->quit()您应该在处理循环中包含一个取消标志(以适应 semahpore 的概念)。通过这种方式,您可以从内部正确地停止工作人员,例如通过停止处理并发出 finished() 信号。

您可以通过在工作人员中实现用于取消(例如slot_cancelWorker())的插槽来设置标志。然后你可以将它与你的 GUI 连接起来:

connect(myPushButton, SIGNAL(clicked()), worker, SLOT(slot_cancelWorker()));

当然,在处理函数内部,您必须检查未决事件。QCoreApplication::processEvents()因此,您必须定期致电。

于 2013-08-07T11:41:42.463 回答