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.
Related
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()));
I have a class ParentThread deriving from QThread with the following run() method that roughly looks as follows:
void ParentThread::run()
{
QThread *childThread = new QThread;
QObject::connect(childThread, SIGNAL(finished()), this, SLOT(onChildThreadFinished());
QObject::connect(childThread, SIGNAL(finished()), childThread, SLOT(deleteLater());
childThread->start();
exec();
}
The slot onChildThreadFinished() is defined on ParentThread, and should run in the context of ParentThread. However, using the code above, onChildThreadFinished only gets called in case the signal/slot connection is a Qt::DirectConnection, but then runs in the context of the child thread. In case the signal/slot connection is defined as a Qt::QueuedConnection, the slot never gets called. I am using Qt 4.8.5. Any idea what the issue is here?
You state that The slot onChildThreadFinished() is defined on ParentThread, and should run in the context of ParentThread. This assumption is wrong. This is one of the reasons why subclassing QThread is discouraged.
If you want to use slots in your threads, subclassing QThread is not what you want to do. Use worker-object method instead. Subclass QObject and call QObject::moveToThread to move your object to a new thread.
Here is what Qt docs say about this:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
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.
In order to ease my cleanup efforts, I want to set the parent of my worker object to be the Qthread to which it is moved. (See below).
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); // Move worker into QThread
TelnetConnectionThread->start();
}
Right before the start() line, I added:
worker->setParent(TelnetConnectionThread);
but at runtime I see an error that I can't do that because the new parent is in a different thread. How can that be? In the line above I moved the worker to the new thread...so the worker should be in the same thread as the TelnetConnectionThread. Help?
I confirmed with some qDebug's and thread() that the worker does in fact get moved to the new thread!
I think you're a bit confused about QThread. The first problem is that its name is rather misleading as it's not actually a thread, but a thread controller. Next is the issue of thread affinity (the thread that an object is actually running on).
If we start in the main thread and create a new QThread, the thread controller is instantiated in the main thread: -
QThread* pThread = new QThread;
Next the thread is started: -
pThread->start();
Even though pThread is thought to be running in a different thread, its thread affinity is still the main thread, but any QObject-based class instance that gets moved to pThread will have the thread affinity of the new thread: -
QObject* pObject = new QObject;
pObject->moveToThread(pThread);
Still, pThread's thread affinity is the main thread, while pObject's thread affinity is the new thread; remember, pThread is actually a thread controller!
To set the parent of pObject to be pThread would be wrong, as they have different thread affinity. This is a problem that many people encounter when trying to inherit from QThread, rather than using it as a separate entity and moving QObjects to it. What usually happens is that objects are sometimes created in the constructor of their inherited QThread class, without parenting them and not realising that those objects will have the thread affinity of the main thread, not the new thread as they expect.
Moving a QObject to another thread also moves its children, so trying to set the parent as the thread (thread controller!) does not make sense.
To summarise, you cannot set the parent of your worker object to be the TelnetConnectionThread, as they run on different threads.
However, if you're trying to have the thread cleanup after itself when finished, you can do this: -
connect(TelnetConnectionThread, SIGNAL(finished()), TelnetConnectionThread, SLOT(deleteLater()));
Normally if I am in a process intensive function I can call QCoreApplication::processEvents() or QEventLoop::processEvents() to ensure that my processing doesn't block other signals and slots.
However, if I create a new QThread and move a worker to that thread, then I don't have a QCoreApplication or a QEventLoop with which to call processEvents().
From my research, it seems that I should be able to install a QEventLoop on the new QThread I created, and then I can call processEvents() on that QEventLoop.
However, I can't figure out how to do this. I figure it might look something like this:
QThread *thread = new QThread(this);
Worker *worker = new Worker(this);
QEventLoop *loop = new QEventLoop();
connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), worker, SLOT(startProcessing()));
connect(worker, SIGNAL(done()), thread, SLOT(quit()));
connect(worker, SIGNAL(done()), loop, SLOT(quit()));
worker->moveToThread(thread);
//loop->exec() // blocks processing of this thread
loop->moveToThread(thread);
//loop->exec() // loop is not a member of this thread anymore and even
// if it was, this would block the thread from starting
thread->start();
//loop->exec(); // loop is not a member of this thread anymore and even
// if it was, this would block this thread from continuing
Every place I try to start the loop has some sort of issue. But even if something like this worked, how would I call processEvents() on that QEventLoop()?
Alternatively, QThread also has a function setEventDispatcher() and QAbstractEventDispatcher has a processEvents() function, but I can't seem to find anything that subclasses QAbstractEventDispatcher.
What is the proper way to process events during an intensive worker function on a QThread?
According to the documentation, calling QCoreApplication::processEvents() processes events for whichever thread called it.
However, if I create a new QThread and move a worker to that thread, then I don't have a QCoreApplication or a QEventLoop with which to call processEvents().
Exactly - don't call it. You don't need it, you don't want it.