Qt - 如何结束 QThread

8
我将尝试终止/取消一个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();
}

我的问题是,在调用线程的 quit 后,线程仍在运行,即 API 的计算没有停止。因为工作线程只调用 API,所以我不能在工作线程循环中使用取消检查。有什么想法吗?
提前感谢!

请看这个答案 - http://stackoverflow.com/questions/14092915/qt-qthread-exclusively-for-object/14178944#14178944,它可能对你有用。 - Ilya Kobelevskiy
5个回答

2
你的重要任务如此简单,我会使用QtConcurrent::run并忘记QThread。
ImageProcessor* worker = new ImageProcessor();
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
QtConcurrent::run(worker, &ImageProcessor::process);

这样做能够精确地达到您的需求,并且速度会更快,因为它将使用线程池中的线程,而不是创建一个新的线程。

只需记得使用 Qt::AutoConnection(默认值)即可。

要优雅地停止处理过程,您必须在进程方法中对某个变量进行轮询:

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();
}

2
如果你有这样的代码,没有任何循环:
void ImageProcessor::process()
{
    worker_api(); // that takes a lot of time
    emit finished();
}

QThread的功能非常有限。你可以采用以下方法,从最好到最差:

  • 或许你正在使用的API有另一个调用来停止工作线程。但是由于你在这里提问,我猜想没有这样的方法。
  • 使用QProcess从不同的进程中启动工作线程,并在不再需要时使用kill终止它。优点:无需担心清理工作。缺点:可能难以检索结果。
  • QThread::terminate,但不建议使用(请参见文档中的警告:基本上没有清理)。
  • 在Windows中,QThread::currentThreadId可以为你提供一个数字,你可以将其与其他操作系统调用一起使用(请参见文档中的警告)。

1
即API的计算不会停止。
所以你要在计算期间尝试停止它?
QThread::quit只能在所有计算完成后才能停止它。
您可以将计算拆分为较小的任务,甚至添加取消标志。在ImageProcessor对象上有一个正常方法来设置它:
class ImageProcessor{
   void stop(){
     canceled = true;
   }
private:
   bool canceled;
}

然后在进程槽中检查if (canceled) return;(一个只写的布尔类型有点线程安全)

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


这里的问题是,我只调用了一个进行大量计算的API函数。我无法将其分解为更小的部分,因此无法检查是否已取消。该API似乎开始了自己的生命周期,我不知道如何停止它... - the_max
1
为什么不让它自己跑呢?如果你调用workerThread->setPriority(QThread::IdlePriority),它也不会占用太多资源... - BeniBela
你可以运行线程直到结束,如果 canceled 为真,则不发出完成信号。 - MichK

1

你不能停止一个已经阻塞的 QThread,至少没有任何安全的方式。

例如,如果你已经进入了这个函数

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

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


0

不要使用 thread->quit(),而是在处理循环中包含一个取消标志(根据信号量的概念进行调整)。通过这种方式,您可以从内部正确地停止工作线程,例如停止处理并发出 finished() 信号。

您可以通过在工作线程中实现一个用于取消的槽(例如 slot_cancelWorker())来设置标志。然后,您可以将其与 GUI 连接:

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

当然,在处理函数内部,您必须检查待处理的事件。因此,您需要定期调用QCoreApplication::processEvents()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接