QThread - problems with it (beginner) - c++

I try to use QThread, but I can't do it :(
My sample thread:
#include "worker.h"
#include "mainwindow.h"
#include <QDebug>
Worker::Worker() {}
Worker::~Worker() {
qDebug() << "Worker ends.";
}
void Worker::run() {
qDebug() << "Worker start.";
sleep(2);
emit finished();
}
And code on_btnStart_clicked():
Worker *worker = new Worker;
QThread *workerThread = new QThread(this);
connect(workerThread, SIGNAL(started()), worker, SLOT(start()));
connect(workerThread, SIGNAL(finished()), worker, SLOT(quit()));
worker->moveToThread(workerThread);
workerThread->start();
Worker starts, but never ends (if I close application, I get QThread: Destroyed while thread is still running.
Another problem - how I can transfer data between thread and my application? I want to use QThread for QLabel (example: timer). I can't find any good tutorial that I can complile without problems.
Anyone can help me?
And, if I can ask, how I can start thread like onCreate() for form? I want to create simple timer to count time of application running.
Regards

You should have a read through This article.
Look at the first code sample of the Qt 4.8 QThread documentation. It has many lines of boiler plate just to run some code in a thread. And the there is even a leak: the QThread is never going to quit and be destroyed.
The submitted patch has been accepted but the href link he forwards you to still shows the old code.
This patch snippet diff shows the valid approach on the right-side.
It shows both methods, sub-classing QThread and using QObject->Worker
You can use that as the sample to base your code on. Apply the approach that suit's your requirements as he mentions in the blog.

Related

Multi-threads cause the runtime error in Qt

When I am trying to do multi-threads in Qt, for example:
If I create a class object in another class like this:
QThread thread;
Worker worker;
worker.moveToThread(thread);
it will cause the runtime error when I close the program.
But if I create the class object by pointer like this:
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
it will be no error. Why do I have to do like this?
And do I have to delete the pointer after using? If I do not, will that cause the memory leak? I see a lot of tutorials delete them like this:
connect(worker, SIGNAL (finished()), thread, SLOT (quit()));
connect(worker, SIGNAL (finished()), worker, SLOT (deleteLater()));
connect(thread, SIGNAL (finished()), thread, SLOT (deleteLater()));
Qt typically manages the memory of its objects. Lots of the documentation says this, but not all of it.
Your runtime error is caused by a double-delete. In the first example, when worker goes out of scope it will be destructed, and Qt will also attempt to delete it. In the second example, only Qt deletes it, and you do not.
If you look towards Qt's own documentation of this, you can see them doing the same thing:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
...
} // notice worker was not cleaned up here, which implies moveToThread takes ownership
However, moveToThread's documentation isn't clear on this.

Properly delete QThread

I'm new to QT and I'm trying to create and destroy a QThread upon button click (potentially multiple times). I've read through a lot of posts but my Thread either didn't get destroyed or caused a untraceable heap exception within the QMain.dll.
I create my Thread like this:
thread = new QThread;
reader = new Reader(); //a QObject subclass
reader->moveToThread(thread);
connect(thread, SIGNAL(started()), reader, SLOT(read()));
connect(reader, SIGNAL(timeout()), this, SLOT(threadTimeout()));
connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
The Thread then runs in a loop and sends data. The read() function looks like this:
void Reader::read() {
while(!stop) {
//... do something ...
}
emit finished();
}
On Button click I call
reader->setStop(true);
which breaks the Thread's while-loop. The finished signal is emitted but then a memory exception is triggered. If I remove the two deleteLater() slots no exception is raised but the thread is (obviously) not deleted.
Am I doing something wrong here? Thanks in advance.
The main issue is that you are deleting the thread object (ie the QThread) while the thread of execution is running. In your threadFinished(), which is actually task finished, you need to do :
thread->quit();
thread->wait();
thread->deleteLater();
reader->deleteLater();
and remove the finished -> deleteLater connections.
Try to add connection like this one for leaving thread event loop.
connect(reader, SIGNAL(finished()), thread, SLOT(quit()));

QTimer not firing in a thread

I have an Qt5 c++ app with 2 threads, thread A is started when the main program starts up. The start method of thread A runs successfully.
So far so good. Next, in the main program I send a signal to Thread A to start a QTimer, which it does - but that timer never expires!
Thread B handles tcp connections. When I initiate a telnet connection to my app, thread B fires up and suddenly I see my Qtimer from thread A expiring at normal intervals.
Why is the QTimer from thread A not expiring until thread B starts?
I suspect my threads are getting messed up. note the last section of code below products this:
thread of this: QThread(0x200fe00)
thread of timer: QThread(0x1fff470)
Which suggest my worker object (this), is in a different thread from my timer object. This timer thread address is actually the MAIN thread. Why? I'm confused.
Suggestions?
In my main app I create and start my thread like this:
QThread * MyControllerThread = new QThread(this);
if (MyControllerThread) {
TheController *worker = new TheController(MyControllerThread);
if (worker) {
connect(MyControllerThread, SIGNAL(started()), worker, SLOT(start()));
connect(MyControllerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(MyControllerThread, SIGNAL(finished()), MyControllerThread, SLOT(deleteLater()));
worker->moveToThread(MyControllerThread);
MyControllerThread->start();
}
and in my main app I emit a signal to the new thread:
emit sig_startlocalpeer(Types::EActionLocalServiceStart); // Move the local peer to standby mode to start remote tests
which runs a slot in my thread (TheController object):
connect(&m_remotetestintervaltimer,SIGNAL(timeout()),this,SLOT(expiredRemoteTestIntervalTimer()));
m_remotetestintervaltimer.setTimerType(Qt::VeryCoarseTimer);
m_remotetestintervaltimer.start(REMOTETEST_TIMER_INTERVAL); // Wait between ticks
qDebug() << "thread of this: " << this->thread();
qDebug() << "thread of timer: " << m_remotetestintervaltimer.thread();
Well, it's not a Qt5 bug, it's more an inaccurate understanding of Qt's thread spirit.
In Qt, you have two ways to implement a thread which are using or not an even loop. Here is just a small visual example.
No event loop
myMethodCalledInANewThread
{
do{ ... }while(...);
}
With an event loop
myMethodCalledInANewThread
{
[...]
exec();
}
(Of course you can mix a do/while with an even loop but stay simple).
In QTimer's doc, you can read:
In multithreaded applications, you can use QTimer in any thread that
has an event loop. [...] Qt uses the timer's thread affinity to
determine which thread will emit the timeout() signal. Because of
this, you must start and stop the timer in its thread; it is not
possible to start a timer from another thread.
So I'm pretty sure you don't have a second event loop in your second thread and that's why you have the behaviour you described.
To give you some tips to be totally clear with thread using Qt, I suggest you to read:
QThread doc: https://doc.qt.io/qt-5/qthread.html
QTimer doc: https://doc.qt.io/qt-5/qtimer.html
and a very good article about how QThread implementation is misunderstood by a lot of users:
You're doing it wrong: https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
I hope it will help ;)
The best answer seems to be a combination of RobbieE and Kuba:
You have to explicitly set the parent of the member variable in constructor. The parent-child feature is a Qt thing that exists among classes derived from QObject, it is not a feature of C++.
I never knew this - I assumed that when an object was created, its members variables automatically had their parent set to the object. Good to know!!

Updating GUI from different thread

I need to have a computationally intensive function run in a different thread so that the GUI doesn't freeze or turn grey when it's running.
I followed this example: https://stackoverflow.com/a/16501374/2904614
But the GUI still freezes and turns grey.
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->textBrowser->setFont(QFont("Monospace",11));
ui->textBrowser->setLineWrapMode(QTextEdit::NoWrap);
updater->moveToThread(thread);
connect(updater,SIGNAL(req()), this, SLOT(getCheckSum()));
connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater()));
thread->start();
}
When the user clicks the designated button. Since there may be a lot of files in one directory, the GUI will freeze as QDirIterator goes through all of them. I'm hoping to add a progress bar, that will show the user the program is still functioning.
void MainWindow::on_pushButton_clicked()
{
updater->getHash();
//getCheckSum();
}
EDIT
I would like to have the function MainWindow::getCheckSum() run in a different thread than the GUI. How will I implement this?
Github: https://github.com/Jyang772/PenguSniff
Given you only need to run one function i would recommend QtConcurrent. Extract form the docs:
Running a Function in a Separate Thread
To run a function in another thread, use QtConcurrent::run():
extern void aFunction();
QFuture<void> future = QtConcurrent::run(aFunction);
This will run aFunction in a separate thread obtained from the default QThreadPool. You can use the QFuture and QFutureWatcher classes to monitor the status of the function.
The problem is that you are calling the function of a class in another thread directly:
updater->getHash();
It causes undefined behavior in your application and makes it crash. The correct way is to emit a signal from your MainWindow which is connected to the getHash() slot in updater:
connect(this,SIGNAL(getHash()), updater, SLOT(getHash()));
void MainWindow::on_pushButton_clicked()
{
emit getHash();
}
This will result in a queued connection and runs the slot in the updater thread.
You call your getHash() computational function (it is computational as per your comment) in GUI thread. Bind it to clicked signal, then it will be invoked in object's thread.
connect(button, SIGNAL(clicked()), this, SLOT(getHash()));

Qt C++ how stop thread if moveToThread

After lots of experimentation and learning from stackoverflow, I've create a QObject worker, a QThread, and moved my QObject worker to my QThread, and started the QThread - and it's working!
void TelnetServer::incomingConnection(qintptr socketDescriptor)
{
QThread * TelnetConnectionThread = new QThread(this);
TelnetConnection *worker = new TelnetConnection(socketDescriptor,TelnetConnectionThread);
connect(TelnetConnectionThread, SIGNAL(started()), worker, SLOT(start()));
connect(TelnetConnectionThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
worker->moveToThread(TelnetConnectionThread);
TelnetConnectionThread->start(); // Start the thread running
}
I assume that calling TelnetConnectionThread->start() starts the eventloop within the QThread (since it seems to be running). Now the problem...how do I stop the thread? I tried:
QThread::quit();
but the thread is still running when I shutdown the app. Does this mean the exec loop is not running? Do I have to do something else to stop this thread? Or is it actually stopped but just not deleted?
It's a bad idea to kill running thread, from design and technical points of view.
Usually the thread must own the decision to quit based on "terminate" flag. For example create new flag "stop", if quit() slot is signaled mark the flag true. In a thread function verify the flag periodically and if it's true - exit thread function.