How to stop QThread?
-
Hi,
I have application with 2 threads ( main and one for long calculations ). Codes:Thread with long calculations:
Worker::Worker(QObject *parent): QObject(parent) { } void Worker::calculate() { for(int i=0; i<10000000;i++) { QCoreApplication::processEvents(); ... } emit endCalc(); }
Main thread ( in one method ):
QThread thread(this); Worker worker; worker.moveToThread(&thread); QObject::connect(&worker, &Worker::endCalc, this, &MainWindow::someSlot); QObject::connect(this, &MainWindow::startCal, &worker, &Worker::calculate); thread.start(); QEventLoop q; connect(&worker, &Worker::endCalc, &q, &QEventLoop::quit); emit startCal(); q.exec(); thread.quit(); thread.wait();
And the problem is that, when I close application when there are still calculations in the second thread ( Worker ), in the main thread I wait in the line
thread.wait();
and my app still running ( gui is closed, but there is still running ). -
-
@J-Hilk I don't understand. I don't want to freeze GUI, so I decided to create the second thread. I need to change asynchronous code to synchronous, so I find solution here:
https://doc.qt.io/archives/qq/qq27-responsive-guis.html ( Waiting in a Local Event Loop )
-
@qwe3 said in How to stop QThread?:
So what if thread don't accept request?
Then it does not terminate.
But sinse it's your thread you can do it the right way, right?
If you don't want to do it right you can still kill your thread using https://doc.qt.io/qt-5/qthread.html#terminate -
@qwe3 said in How to stop QThread?:
. I don't want to freeze GUI
But you do: q.exec();
So, you're waiting in your main thread for the other thread to terminate... -
@qwe3 said in How to stop QThread?:
And the problem is that, when I close application when there are still calculations in the second thread ( Worker ), in the main thread I wait in the line thread.wait(); and my app still running ( gui is closed, but there is still running ).
Your design looks to me very complicate for such a "trivial" use case.
Why you are not usingQtConcurrent::run()
?
In addition withQFuturWatcher
you could be informed about calculation end and got result.Take a look at https://doc.qt.io/qt-5/qtconcurrentrun.html.
Or did I miss something?
-
@KroMignon But can I send signals from the second thread ( long operations ) like current progress? I never used QtConcurrent.
@jsulm Yes, I have to. I changed code and ... it doesn't work. Now it looks like this:
Worker::Worker(QObject *parent): QObject(parent) { } void Worker::calculate() { for(int i=0; i<10000000;i++) { QCoreApplication::processEvents(); if( QThread::currentThread()->isInterruptionRequested()) { qDebug()<<"return"; return; } ... } emit endCalc(); }
And in main thread ( thread is now a pointer ):
thread->start(); QEventLoop q; connect(&worker, &Worker::endCalc, &q, &QEventLoop::quit); emit startCal(); q.exec(); thread->requestInterruption(); thread->quit(); thread->wait();
When I close the app using X button, I see on debuq "return", but application is still wait on
thread->wait();
-
@qwe3 said in How to stop QThread?:
connect(&worker, &Worker::endCalc, &q, &QEventLoop::quit);
emit startCal(); q.exec(); thread->requestInterruption();
Are you aware that requestInterruption() will be send to thread after it finished? Because q.exec() blocks until endCalc is emitted. What is the point of doing so?
Also, you should either emit this endCalc also when termination is requested or use https://doc.qt.io/qt-5/qthread.html#finished signal to terminate the local event loop.
-
@qwe3 said in How to stop QThread?:
but q.exec() is done
How can it be done and how did you verify it is done? The only condition in your code to terminate the local event loop is endCalc signal...
-
@jsulm I debuq informations like this:
second thread:
void Worker::calculate() { for(int i=0; i<10000000;i++) { QCoreApplication::processEvents(); if( QThread::currentThread()->isInterruptionRequested()) { qDebug()<<"return"; return; } ... } qDebug()<<"before emit endCalc"; emit endCalc(); }
In main thread:
q.exec(); qDebug()<<"after exec"; thread->requestInterruption(); qDebug()<<"after requestInterruption"; thread->quit(); qDebug()<<"after quit"; thread->wait();
When I close application I see on debuq:
after exec after requestInterruption after quit return
But it is still waiting on
thread->wait();
So I don't emit endCalc(), but Local Event Loop is done ( of course this event loop is the second event loop in my app - the first one is in main.cpp and this one ( the second one ) is in my own class method ).
-
@qwe3 said in How to stop QThread?:
But can I send signals from the second thread ( long operations ) like current progress? I never used QtConcurrent.
I have no clue about what you try to do, so here is a "generic" way to do.
Create a QRunnable class, for example:class Work : public QRunnable { public: Work():mPromise(nullptr) {} ~Work() { delete mPromise; } void run() { int loopEnd = qrand(); int loopCount = 0; if(mPromise) { mPromise->reportStarted(); mPromise->setProgressRange(0, loopEnd); mPromise->setProgressValue(0); } QTextStream out(stdout); while (loopCount < loopEnd) { ++loopCount; if(mPromise) { if(mPromise->isCanceled()) break; mPromise->setProgressValueAndText(loopCount, QString("Importing %1 frame from: %2...").arg(loopCount).arg(loopEnd)); } out << "loopCount: " << loopCount << endl; QThread::msleep(250); } if(mPromise) mPromise->reportFinished(); } QFuture<void> getFuture() { // create future interface to be able to send progression if(!mPromise) { mPromise = new QFutureInterface<void>(); mPromise->setThreadPool(QThreadPool::globalInstance()); mPromise->setRunnable(this); } return mPromise->future(); } private: QFutureInterface<void> *mPromise; };
And then use it, like this for example:
int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QFutureWatcher<void> futureWatcherProgress; // ==> stop application when future is done QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::finished, &app, &QCoreApplication::quit); // ==> stop future on application exit QObject::connect(&app, &QCoreApplication::aboutToQuit, &futureWatcherProgress, &QFutureWatcher<void>::cancel); // follow progression QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::progressValueChanged, &app, [](int progressValue){ QTextStream out(stdout); out << "Progression is" << progressValue << endl; }); QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::progressRangeChanged, &app, [](int minimum, int maximum){ QTextStream out(stdout); out << "Progression range from" << minimum << "to" << maximum << endl; }); QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::progressTextChanged, &app, [](const QString &progressText) { QTextStream out(stdout); out << "Progression:" << progressText << endl; }); // create worker ==> QRunnable are automatically destroyed when finished (cf QRunnable::autoDelete()) auto runnable = new Work(); futureWatcherProgress.setFuture(runnable->getFuture()); return app.exec(); }
-
@KroMignon Thank you for your code, but I don't understand how that can help me.
I change a little your code ( I never before used QFuture etc. so maybe I do something wrong ):
runnable->run();
afterfutureWatcherProgress.setFuture(runnable->getFuture());
so I have:... auto runnable = new Work(); futureWatcherProgress.setFuture(runnable->getFuture()); runnable->run();
And I add MainWindow like this:
QApplication app(argc, argv); MainWindow w; w.show(); return app.exec();
And move code, which was in main.cpp to mainWindow like this:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); QTimer::singleShot(1000, this, &MainWindow::fun1); } MainWindow::~MainWindow() { delete ui; } void MainWindow::fun1() { QFutureWatcher<void> futureWatcherProgress; // ==> stop application when future is done QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::finished, qApp, &QCoreApplication::quit); // ==> stop future on application exit QObject::connect(qApp, &QCoreApplication::aboutToQuit, &futureWatcherProgress, &QFutureWatcher<void>::cancel); // follow progression QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::progressValueChanged, qApp, [](int progressValue){ QTextStream out(stdout); out << "Progression is" << progressValue << endl; }); QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::progressRangeChanged, qApp, [](int minimum, int maximum){ QTextStream out(stdout); out << "Progression range from" << minimum << "to" << maximum << endl; }); QObject::connect(&futureWatcherProgress, &QFutureWatcher<void>::progressTextChanged, qApp, [](const QString &progressText) { QTextStream out(stdout); out << "Progression:" << progressText << endl; }); // create worker ==> QRunnable are automatically destroyed when finished (cf QRunnable::autoDelete()) auto runnable = new Work(); futureWatcherProgress.setFuture(runnable->getFuture()); runnable->run(); }
And this code freeze GUI ( mainWindow ). I add second thread to unfreeze GUI.
-
@qwe3 said in How to stop QThread?:
I change a little your code ( I never before used QFuture etc. so maybe I do something wrong ):
runnable->run(); after futureWatcherProgress.setFuture(runnable->getFuture());Okay, my code was o a little bit too rough, so I will be more explicit:
- a create a class based on
QRunnable
to be able to use it withQThreadPool
. - in the method
getFuture()
, the class instance is transformed to future a will run in next thread available the thread pool. So therun()
method will be called a soon as possible - after that I record the
QFuture
instance returned into theQFutureWatcher
instance to be aware about state changes.
Your
fun1()
don't made sense; theQFutureWatcher
is a local variable which will be delete at function end!
Change it like this:void MainWindow::fun1() { // do NOT use a local variable!!! auto futureWatcherProgress = new QFutureWatcher<void>(); // ==> delete QFuteWatcher instance instance when process done connect(futureWatcherProgress, &QFutureWatcher<void>::finished, futureWatcherProgress, [futureWatcherProgress]() { futureWatcherProgress->deleteLater(); qDebug() << "Processing done."; }); // ==> stop future on application exit connect(qApp, &QCoreApplication::aboutToQuit, futureWatcherProgress, &QFutureWatcher<void>::cancel); // follow progression connect(futureWatcherProgress, &QFutureWatcher<void>::progressValueChanged, this, [](int progressValue){ qDebug() << "Progression is" << progressValue; }); connect(futureWatcherProgress, &QFutureWatcher<void>::progressRangeChanged, this, [](int minimum, int maximum){ qDebug()<< "Progression range from" << minimum << "to" << maximum; }); connect(&futureWatcherProgress, &QFutureWatcher<void>::progressTextChanged, qApp, [](const QString &progressText) { qDebug() << "Progression:" << progressText; }); // create worker ==> QRunnable are automatically destroyed when finished (cf QRunnable::autoDelete()) auto runnable = new Work(); // register future and start processing futureWatcherProgress->setFuture(runnable->getFuture()); }
- a create a class based on