How to use QThread
-
I want a Worker class with a QThread that i construct, use and then destruct, i want everything encapsulated in the TaskWorker class (because you cannot have everything in one class if you want to wait for the thread).
I have following Worker class:
class Worker : public QObject { Q_OBJECT public: Worker() {}; ~Worker() {}; public slots: void process() { qDebug("Hello World!"); std::this_thread::sleep_for(std::chrono::seconds(5)); qDebug("Bye World!"); emit finished(); }; signals: void finished(); };
And this is the TaskWorker class:
class TaskWorker : public QObject { Worker* worker; QThread* thread; Q_OBJECT public: TaskWorker() { thread = new QThread(); worker = new Worker(); worker->moveToThread(thread); connect(thread, &QThread::started, worker, [=](){worker->process(); qDebug() << "connection 1";}); connect(worker, &Worker::finished, thread, [=](){thread->quit(); qDebug() << "connection 2";}); connect(worker, &Worker::finished, worker, [=](){worker->deleteLater(); qDebug() << "connection 3";}); connect(thread, &QThread::finished, thread, [=](){thread->deleteLater(); qDebug() << "connection 4";}); thread->start(); } ~TaskWorker() { qDebug() << "~CoolWorker"; if (thread->isRunning()) { qDebug() << "isRunning"; thread->quit(); thread->wait(); } delete thread; delete worker; } };
Problems:
- Closing the App while the process() method is running leads to following output:
Hello World! ~CoolWorker isRunning Bye World! connection 3 connection 1 Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
The interesting thing is that "connection 1" is printed as last thing, this is connected to QThread::started
- Wait for the Process method to finish and then close the app leads to the following:
Hello World! Bye World! connection 3 connection 1 connection 2 connection 4 ~CoolWorker Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
I wan't the app to wait for all threads to finish and then close.
Thanks a lot for your help :)
-
@0verEngineer said in How to use QThread:
delete thread;
delete worker;You should use deleteLater()
thread->deleteLater(); worker->deleteLater();
-
@jsulm said in How to use QThread:
You should use deleteLater()
@0verEngineer mustn't call delete here, calling delete on a non nullptr already deleted object is UB
also, don't use this
std::this_thread::sleep_for(std::chrono::seconds(5));
to "simulate" work being done it is messing things up. connects etc require the event loop, same as wait() calls
-
@J-Hilk Hey, thanks a lot for your help, however i removed the delete statements, commented out the work simulation sleep and changed the thread->isRunning check in the destructor.
The class looks now like that:
class TaskWorker : public QObject { Worker* worker; QThread* thread; Q_OBJECT public: TaskWorker() { thread = new QThread(); worker = new Worker(); worker->moveToThread(thread); connect(thread, &QThread::started, worker, [=](){worker->process(); qDebug() << "connection 1";}); connect(worker, &Worker::finished, thread, [=](){thread->quit(); qDebug() << "connection 2";}); connect(worker, &Worker::finished, worker, [=](){worker->deleteLater(); qDebug() << "connection 3";}); connect(thread, &QThread::finished, thread, [=](){thread->deleteLater(); qDebug() << "connection 4";}); thread->start(); } ~TaskWorker() { qDebug() << "~CoolWorker"; if (thread && thread->isRunning()) { qDebug() << "isRunning"; thread->quit(); thread->wait(); } } };
Output on close:
Hello World! Bye World! connection 3 connection 1 connection 2 connection 4 ~CoolWorker
Then it just hangs and does not close
Remaining Problems:
-
Why is "connection 1" printed after "Hello World!", "Bye World!" and "connection 3"?
-
Why does the app not quit this way - it just hangs?
-
I know that the sleep is blocking the event loop (what i did not know is that wait() need the event loop too) but i mean if i have this worker with a slot doWork() and i start it per signal then the event loop is also blocked until doWork() is finished, so i don't get why the sleep() call to simulate work is a bad thing if the real work is also blocking the event loop?
-
-
@0verEngineer
Where are you running the Qt event loop? To send signals and for slots to run when cross-thread requires this to process the signals/slots.Depending, it is possible that
connection 1
would be output earlier if you moved it to before theworker->process()
call.Why does the app not quit this way - it just hangs?
You show a thread running to conclusion. You show nothing about the main/UI thread exiting.
-
@JonB OMG, how dumb :D But thats what happens when you try to get stuff working without a pause :D
I moved the debug outputs before the calls now and the output is now ordered correctly :DThis is just a Test so i have created a new Test-Project and i have the TestWorker as member in the MainWindow:
QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT TaskWorker taskWorker; public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; };
-
@0verEngineer said in How to use QThread:
Why is "connection 1" printed after "Hello World!", "Bye World!" and "connection 3"?
You write that it should first do
process()
and only after that print "connection 1".process()
itself prints "Hello World!" and "Bye World!". After that it emitsfinished()
which will most likely be a direct connection by default because it is all within the same thread. This means that immediately the slot forWorkerThread::finished
will be executed and print "connection 3". Now,process()
returns and only then "connection 1" can be printed. "connection 2" is printed later becausethread
is in a different thread thanworker
. This makes it a queued connection and thus could be executed in any order.