Reputation: 195
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
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:
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).QThread::currentThreadId
gives you a number you may be able to use with other OS calls (see the warnings in the documentation).Upvotes: 2
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
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
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
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