I wanted to create QObject (object) with the child QThread (thread) with that object as parent (for keeping thread alive while object is alive) and make object.moveToThread(thread) but signal to start the thread isn't working in this case.
Simply:
object owns thread
object moves to thread
signal starting thread isn't working
What's going on?
[Edit]: Throwing away my initial answer due to the comments
Maybe do it like the following:
Create the Object
Create the Thread, but don't assign a parent to it
Connect the Thread's finished() signal to its deleteLater() slot as usual
Connect the Object's deleted() signal to the thread's stop() slot
Then, when you delete the Object, it will emit deleted() which will stop the thread. The thread will emit finished() which will call its deleteLater() slot.
Related
I want to terminate (finish) a QThread, when my app will be destroyed.
So I invoke terminate() and quit() within a destructor of my derived class from `QThread.
Is it safe?
class Session : public QThread
{
Q_OBJECT
private:
CheckerAdapter *inst;
public:
explicit Session(CheckerAdapter *inst, QObject *parent = 0);
void run();
~Session(){
terminate();
quit();
}
};
Using QThread::terminate() can lead to memory corruption, since the thread is just terminated without its knowledge, it can be doing anything while it gets terminated:
Warning: This function is dangerous and its use is discouraged. The thread can be terminated at any point in its code path. Threads can be terminated while modifying data. There is no chance for the thread to clean up after itself, unlock any held mutexes, etc. In short, use this function only if absolutely necessary.
To safely terminate a QThread, You need to have a way to tell the thread that it has to terminate, and when the thread gets that, it should return from its run() implementation as soon as possible. Qt provides two ways to do this:
If your thread runs an event loop (i.e. You don't override run(), Or if you call exec() in your custom run() implementation), You can call QThread::quit()/QThread::exit() from any thread. This will cause the thread event's loop to return as soon as it finishes processing current events. There is no data corruption, as current processing doesn't get terminated.
If your thread does not run an event loop, You can use QThread::requestInterruption() from any other thread to tell the thread that it should stop. But you have to handle that in your implementation of run() using isInterruptionRequested()(otherwise, calling requestInterruption() will do nothing).
Note:
If you are using any of the above methods to stop your QThread in its destructor, You have to make sure that the thread is no longer running after the QThread object gets destructed, You can do that by calling QThread::wait() after using quit()/requestInterruption().
Have a look at this answer for a similar implementation of a QThread subclass.
If I understand correctly, slots will always occur in the main thread.
So what is the difference between using the signal-slot system and moveToThread(qApp->thread())?
Your understanding is incorrect.
Each thread has its own event queue, so when a signal is emitted, if the connection is queued (not direct), it will be added to the event queue matching the thread affinity of the object.
For example: -
Let's assume that we have 2 objects; object1 running on the main thread and object2, which has been moved to a new thread.
connect(object1, &SomeObject::signal1, object2, &SomeOtherObject::signal2);
When object1 emits signal1, an event is posted to the new thread; the thread to which object2 was moved.
When the new thread processes its event loop and the event for signal1 it will execute object2's slot, signal2. This is not on the main thread.
Each QObject tree can be assigned to a specified thread. moveToThread means move tree of objects (for given root object) to that thread.
That doesn't mean that that all code of the QObject is assigned to that thread. It means that any slot invoked by queued connection (not direct connection) will be invoked in given thread. Read carefully documentation of QObject::connect and Qt::ConnectionType.
Except for Qt::DirectConnection, all connections use the owning thread of the receiving object to deliver the signal. All arguments are packed and sent to the receiver's event queue. When the receiving thread's event loop gains control it will unpack the arguments and invoke the slot.
Note that I wrote above about a tree of objects. You can't move an object to a different thread if it has a parent. And you can't reparent an object to one that belongs in a different thread.
By default objects are assigned to thread which created them (if they do not have a parent). So it doesn't have to be main thread.
Thread a,b,c;
a created b, b created c;
connect(a, &QThread::finished, a, &QObject::deleteLater);
connect(b, &QThread::finished, b, &QObject::deleteLater);
connect(c, &QThread::finished, c, &QObject::deleteLater);
I do some experiment, found that if b had finished faster than c, c would not be deleted; But can do that:
b->moveObject(a); //then b would be deleted again.
I have a class, audio_engine_interface, and in main.cpp, I add it to the QML thing.
viewer.rootContext()->setContextProperty("engine", engine);
In audio_engine_interface, I have a audio_engine class, which is computationally intensive—it needs to run on its own thread.
void audio_engine_interface::play()
{
QThread thread;
thread.start();
engine->moveToThread(&thread);
engine->play(); // Will use 100% of CPU
}
However, when I do this, the whole QML thread locks up, meaning I can't pause (pretty important). Am I missing something?
EDIT:
This thread won't mess up anything or access objects from other places. However, it does have a pause function that will need to be called at some point. For what it's worth, the engine is doing pitch shifting.
This is a problem: -
Qthread thread;
Creating a QThread object like this is creating it on the stack. When the function ends, the object will go out of scope and delete the QThread object.
You need to dynamically allocate the object on the heap: -
QThread* thread = new QThread;
Then remember to delete the thread, or set it to delete itself: -
//Qt 5 connect syntax
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
You should also be aware of thread affinity (the thread which an object is running on). I suggest reading this article on how to use QThread properly.
You have so many problems.
when you move to thread your object must not have a parent
your thread object is local variable so it will day immediately when udio_engine_interface::play() end execution
you are invoking you engine->play(); method directly and this means that it will be executed in current thread.
moveToThread means that slots invked by signals connected using default 5th parameter (Qt::AutoConnection) will be queued in event loop of given thread.
The easiest way to fix it is use QtConcurrent:
void audio_engine_interface::play()
{
QtConcurrent::run(engine, &EngineClass::play);
}
Depending what your engine does you should make it thread safe (use mutex locks an so on), without details it is hard to tell, what exactly you should do.
Which is more appropriate: QThread or QFuture ?
I'm trying to pass a QNetWorkAccessManager in a QThread but it causes an error with the parent and child thread :/
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0xc996cf8), parent's thread is QThread(0xaba48d8), current thread is Citizen(0xca7ae08)
m_networkManger = new QNetworkAccessManager(this);
m_thread = new QThread();
m_data = new LoadData(m_labelsList, m_parserType, argUrl, m_networkManger);
m_data->moveToThread(m_thread);
connect(m_thread, SIGNAL(started()), m_data, SLOT(process()));
connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater()));
m_thread->start();
Could using QFuture solve the problem?
My first thought was why do you want to move the networkManager to a different thread anyway?
Anyhow, the problem you see is that when you move an object to a new thread, it moves the object and its children.
You create m_networkManager and pass 'this' to it, making that object its parent. Whatever that object is, it will be residing on the original thread. You can't move a child object to a different thread from its parent.
Therefore, remove the parent object 'this' when creating the QNetworkAccessManager.
m_networkManger = new QNetworkAccessManager;
Ensure you handle deleting of the networkManager, now that it is no longer parented.
The object which is moved to other thread must not have a parent. Qt is designed in such way that whole object tree must be in one thread (object is moved to other thread with all its children).
If object has a parent it is moveToThread will fail (do nothing only prints error in logs).
QFuture doesn't change anything in this case.
Note that you can run object methods in different thread then object belongs to. If objects belong to some thread it means that connected slots of this object will tend to be called from this thread (only if connection type was Qt::DirectConnection slot can be invoked from different thread).
I have a subclass of QObject referred to as myObject, which has a QTimer data member allocated on the heap in the constructor. myObject also has a slot which is connected to the QTimer timeout() signal in the constructor. I refer to the pointer of myObject as myObject_ptr.
I want to run myObject on a different thread from the main thread. Following the relatively new recommendations, I DO NOT subclass QThread. In the main thread, I use myObject as follows:
QThread *thread = new QThread(this);
myObject_ptr->moveToThread(thread);
connect(myObject_ptr, SIGNAL(destroyed(), thread, SLOT(quit())); //thread not needed if no object
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); //to avoid memory leak
connect(thread, SIGNAL(terminated()), thread, SLOT(deleteLater())); //to avoid memory leak
thread->start();
The main thread invokes a function of myObject_ptr which in turn starts the QTimer data member. When it times out, nothing happens, but I expect the slot of myObject to which the timer's timeout() signal is connected to be invoked. What is the problem? How do you make this work. It works flawlessly if myObject is run on the same thread where it was created i.e. main thread.
From all the readings I've done, I think the new thread I am creating might not be processing events because it doesn't have it's own event loop. I also read documentation/articles contrary to that, saying that when the thread starts, the run() function calls exec() and you have an event loop.
Could someone help me please?
I could probably get it to work correctly if I subclass QThread, but based on current recommendations, I would prefer to avoid doing that.
Thank you in advance.
I solved my problem!! In the constructor of MyObject, the timer is allocated on the heap as follows:
timer_ptr = new QTimer(this);
but to work correctly, it should be:
timer_ptr = new QTimer(0);
and in the destructor, delete the object manually:
timer_ptr->deleteLater();
I guess when they say can't move an object with a parent to a thread, they really do mean ALL objects, including data members of the object actually being moved to the new thread.
Happy coding.