the_max
the_max

Reputation: 195

Qt - how to kill QThread

I'm trying to kill/cancel a QThread. I followed the implementation of https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

I create the QThread like this:

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

and try to stop it on a certain UI event by calling:

workerThread->quit();

My worker looks like this:

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

signals:
void finished();

public slots:
void process();

private:
//...
};

Where process calls an API with heavy calculations

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

emit finished();
}

My problem is, that after calling quit on the thread, the thread keeps running ie the calculations of the API don't stop. Because the worker only calls the API, I cannot work with a cancel check in the worker loop. Any ideas?

Thanks in advance!

Upvotes: 8

Views: 14588

Answers (5)

dmcontador
dmcontador

Reputation: 668

If you have something like this, without any loop:

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

There is little you can do with QThread. You can do, from probably better to worst:

  • Maybe the API you are using has another call to stop that worker call. But since you are asking here, I guess there is no such thing.
  • Launch the worker call from a different process using QProcess and kill it when no longer needed. Pros: no worries about clean-up. Contras: retrieving results may be difficult.
  • QThread::terminate, but is unadvised (see the warnings in the documentation: basically, no clean-up).
  • In Windows, QThread::currentThreadId gives you a number you may be able to use with other OS calls (see the warnings in the documentation).

Upvotes: 2

Marek R
Marek R

Reputation: 38161

Your heavy task is so simple that I would use QtConcurrent::run and forget about QThread.

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

This does exactly what you need and it will be even a bit faster since it will use thread from thread pool, instead creating a new thread.

Just remember to use Qt::AutoConnection (default value).

To stop processing gracefully you have to do a poling of some variable in your process method:

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

Upvotes: 2

Kris
Kris

Reputation: 1

Instead of using thread->quit() you should include a cancel-flag inside your processing loop (in adaption to the concept of semahpore). By this means you are able to stop the worker properly from inside, e.g. by stopping your processing and emitting the finished() signal.

You can set the flag by implementing a slot for canceling (e.g. slot_cancelWorker()) within the worker. Then you can connect it with your GUI:

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

Of course, inside you processing function you have to check for pending events. Therefore you have to call QCoreApplication::processEvents() periodically.

Upvotes: 0

sashoalm
sashoalm

Reputation: 79685

You cannot stop a QThread that has blocked, at least not in any safe way.

For example, if you have entered this function

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

the thread will not respond to any quit message, and while most OSes have a way to force termination of a thread, any allocated memory will not be properly released.

Upvotes: 2

BeniBela
BeniBela

Reputation: 16927

ie the calculations of the API don't stop.

So you try to stop it during a calculation?

QThread::quit only stops it after everything has been calculated.

You can split the calculation into smaller tasks, or even add a cancel flag. Have a normal method on the ImageProcessor object that sets it:

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

and then check for if (canceled) return; in the process slot. (a write-only bool is kind of thread safe)

Then call stop(); on the object and quit() on the thread.

Upvotes: 1

Related Questions