QThread emits finished() signal but isRunning() returns true and isFinished() returns false
The way the QThread lifecycle works is like this:
- You call
QThread::start()
. - At this point,
isRunning()
should start returning true. - The thread internals start. They emit the
started()
signal. - The thread internals call
run()
. - Unless you override this in a subclass,
run()
callsexec()
. exec()
enters an event loop and stays there untilquit()
orexit()
is called.exec()
andrun()
return to the internals.- At this point,
isFinished()
should start returning true andisRunning()
false. - The internals emit the
finished()
signal. - The internals do some final cleanups.
- The thread terminates for real.
So you need to call quit()
after your location fetcher is done - but this->quit()
isn't calling quit()
on the thread! This is probably why it's not doing anything.
Your code looks a bit like it was patterned after this article:
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
Note how she gives her worker a finished()
signal (not the same as QThread::finished
) and connects it to the QThread::quit()
slot.
QThread.isFinished returns False in slot connected to finished() signal
Turns out it was a bug in qt (https://bugreports.qt-project.org/browse/QTBUG-30251) which is fixed in Qt 4.8.5.
PySide 1.2.1 comes with Qt4.8.5.
QThread finished state
No. The finished state is there to indicate that before that quit()
or exit()
have been called. It is an alternative to receiving the finished()
signal.
The quit()
and exit()
lead to run()
(which is the only part of the thread that actually runs in a separate thread) returning to the thread that your QThread
instance belongs to. After that a cleanup procedure is executed - events beside the designated deletion ones can no longer be processed.
The finished state is triggered and you can safely remove the instance of your QThread
. finished()
signal is emitted (in most cases connected to deleteLater()
slots of the objects that reside in the separate thread) and isFinished()
returns true
.
Naturally if you haven't started the thread it cannot be finished because it never ran. :D Joke aside, it is perfectly safe to remove an instance of QThread
that hasn't been started at all.
If you are more interested in this I suggest you look at the source code of QThread
to see how the class is sturctured and its implementation works.
Why can a sub-classed QThread simply fail to start?
There is a race condition in the version of Qt I am using. I don't know if it was reported or not before, but I do not have the latest, so it's probably moot unless I can demonstrate it in the current version.
Similar bugs were reported here long ago:
QThread.isFinished returns False in slot connected to finished() signal
(the version I use is much more recent than Qt 4.8.5)
What more important is I can workaround it with the following code
while ( isRunning() )
{
msleep(1);
}
start();
I've run a few tests, and it never seems to take more than 1ms for the race condition to settle. Probably just needs a context switch to clean up.
General QThread understanding: quit() and isRunning()
I can be wrong, but documentation tells us that quit and exit(int ) finishes thread event loop. But exiting thread event loop doesn't mean that thread is finished. You can even run another qthread without even starting event loop by subclassing and reimplementing run method. So calling quit method doesn't mean that thread isFinished() return true. Just connect yourself to finished() signal and do what you wanted with finished thread.
QThread finished() signal is never emited
You have a forever loop:
void StartWork()
{
running = true;
while(running)
{
do work;
}
}
This function will loop, so the event loop of the thread is blocked just after having emitted started()
. Thus, finished()
cannot be emitted.
Solution 1:
Add the function
void DoWork()
{
if(running)
{
do work
}
}
To Worker, and change
void StartWork()
{
running = true;
}
Then, just connect DoWork
to a timer in the thread.
Solution 2:
Change the function
void StartWork()
{
running = true;
while(running)
{
do work;
QCoreApplication::processEvents();
}
}
With this solution, you will have to restart the thread when the worker stops its job (StopWork()
will force this function to finish, so the event loop will have no events to handle and the thread will be finished).
QThread won't stop / does not process a signal
I believe the issue is that you actually don't have Qt event loop running in the scenario. If you replace testThread.wait()
with a.exec()
(which starts the Qt event loop) you should see your signals/slots getting processed properly.
If you want the application to quit when your thread has finished you can use QCoreApplication's quit()
slot to do so.
Is it necessary to delete an object if that runs on a QThread if finished signal of QThread is connected to deleteLater?
No it is not necessary. The connection should do exactly what you want as long as the thread does emit finished eventually. See the following Code:
#include <QObject>
#include <QThread>
#include <iostream>
class Test : public QObject {
Q_OBJECT
public:
Test(){
std::cout <<"c'tor" << std::endl;
}
~Test(){
std::cout << "d'tor" << std::endl;
}
};
void startThread(){
QThread* thr = new QThread();
Test* test = new Test();
connect(thr, &QThread::finished, test, &QObject::deleteLater);
test->moveToThread( thr );
thr->start();
thr->quit();
thr->wait();
delete thr;
}
The output is as expected:
c'tor
d'tor
Note that the snippet wont compile right away since its missing a main.
Unable to connect between QThread with finished() signal for multiple Qthread
First of all you should note that the default connection type is Qt::AutoConnection
. This means if signal is emitted from a different thread than the receiving object's thread, Qt::QueuedConnection
is used. In this case: The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
So you need an event loop.
It works with 2 threads because you probably have an event loop running in your main thread. In your case where you use only thread2
and thread3
objects, thread2
object will actually live in the main thread, while thread3
object will live in the thread managed by the thread2
object. So slots in thread2
object should work.
But in the case of 3 threads, thread1
object would live in the main thread, thread2
object would live in the thread managed by thread1
object, and because there is no running event loop there, the slot in thread2
object will never be executed.
You can call QThread::exec() in your QThread::run()
function, but note that the slots will be executed in the thread where your QThread
object lives in, not the thread it manages. Because of this you shouldn't use slots in QThread
subclasses. You should create a QObject
subclass and move it to a thread.
Another option is to use Qt::DirectConnection
for the connection type, when you connect your signals to slots.
Related Topics
How to Use Makefiles in Visual Studio
Is a Member of an Rvalue Structure an Rvalue or Lvalue
Using Pair as Key in a Map (C++/Stl)
Real-Time Pitch Detection Using Fft
Converting Cv::Mat for Tesseract
Correct Way to Inherit from Std::Exception
Is the Std::Set Iteration Order Always Ascending According to the C++ Specification
Resolving a Circular Dependency Between Template Classes
Why Does Initializing an Extern Variable Inside a Function Give an Error
Difference Between File Scope and Global Scope
Two Dimensional Array Using Vector
Filesystem' with C++17 Doesn't Work on My MAC Os X High Sierra
Llvm Get Constant Integer Back from Value*
Symbol Visibility and Namespace
Why Can't You Do Bitwise Operations on Pointer in C, and Is There a Way Around This
Which MACro to Wrap MAC Os X Specific Code in C/C++