[Solved] QObject back to main thread
-
Hi there,
I wonder how to get back an object running in a QThread to the main thread?
The following code runs in the main thread:
@QThread *thread = new QThread();
MyObject *object = new MyObject();
object->moveToThread(thread);
thread->start();@
I would like to get back the object instance to the main thread. I've tried (from the main thread):
@object->moveToThread(QApplication::instance()->thread());@
But I've got: @QObject::moveToThread: Current thread (0x00000001) is not the object's thread (0x00000002). Cannot move to target thread (0x00000002)@
I've also tried to do it from the object itself:
@moveToThread(QApplication::instance()->thread());@
Same error...
Any idea?
-
Please read the "warning":http://qt-project.org/doc/qt-4.8/qobject.html#moveToThread posted in the moveToThread documentation. You must move the object from the thread you created. You cannot pull it back into your thread from your main thread. Also your 2 tries are actually just one try as your moveToThread is called in your mainThread not in your worker thread.
-
[quote author="b1gsnak3" date="1366208403"]Please read the "warning":http://qt-project.org/doc/qt-4.8/qobject.html#moveToThread posted in the moveToThread documentation. You must move the object from the thread you created. You cannot pull it back into your thread from your main thread. Also your 2 tries are actually just one try as your moveToThread is called in your mainThread not in your worker thread.[/quote]
Yes I read this but how to do?
I am obliged to subclass QThread then, aren't I?
-
[quote author="Maxbester" date="1366206856"]I've also tried to do it from the object itself:
@moveToThread(QApplication::instance()->thread());@
Same error...[/quote]Hi,
This piece of code will work, IF it is called from the other thread.
Keep in mind, when you move a QObject to another thread, the only guarantee you get is this: The QObject's slots will run in the other thread, if invoked through a queued connection.
Example:
@
class MyObject : public QObject
{
public slots:
void doWork();
}//--------
void OtherObject::foo()
{
this->obj = new MyObject();
this->thread = new QThread(this);obj->moveToThread(thread); connect(this, SIGNAL(startWork()), obj, SLOT(doWork())); thread->start(); // ====================================== // Two ways of calling MyObject::doWork() // ====================================== obj->doWork(); // Direct function call; runs doWork() in the main thread. BAD. emit startWork(); // Queued invokation; runs doWork() in the other thread
}
@So, you can make the object move itself, by putting moveToThread() in a slot and invoking that slot.
But before that, it might be more useful to ask: Why do you need to move the object back to the main thread?
-
Okay thanks for your reply. It makes sense.
For my knowledge, is it possible to call a slot with:
@QMetaObject::invokeMethod(obj,"doWork", Qt::QueuedConnection)@?
Actually, I want to move back the object to the main thread when the user closes a modal window. The thread has been launched by this modal window and I don't want to lose the object when closing it. So I reimplemented the event function to send a stop signal to the object. When the object receives it, it does the moveToThread(QApplication::instance()->thread).
The problem is when I send the stop() signal in a queue connection, it is received 52 seconds after the user asked to close the window...
Can you think of a work around?
Thanks
-
[quote author="Maxbester" date="1366273841"]Okay thanks for your reply. It makes sense.
For my knowledge, is it possible to call a slot with:
@QMetaObject::invokeMethod(obj,"doWork", Qt::QueuedConnection)@?
[/quote]You're welcome :) Yes you're right, QMetaObject::invokeMethod() will cause the slot to run in the other thread too.
[quote]
Actually, I want to move back the object to the main thread when the user closes a modal window. The thread has been launched by this modal window and I don't want to lose the object when closing it. So I reimplemented the event function to send a stop signal to the object. When the object receives it, it does the moveToThread(QApplication::instance()->thread).The problem is when I send the stop() signal in a queue connection, it is received 52 seconds after the user asked to close the window...
Can you think of a work around?
Thanks[/quote]52 seconds is a very long time. It sounds like something is blocking the thread's event loop. Does your object run a long for/while loop? Is there a function that takes a long time to return? (If a function doesn't return, then signals that are queued in the event loop can't be processed -- they will wait until the function returns).
May I ask what kind of processing do you do in the thread that's launched by the modal window?
-
Yes this is a very long time. I am trying to find if a function doesn't return but for now I cannot find any. I don't have any loop. I hope I don't need any to use threads... Do I?
My application is a bit complex. The modal window aims to inform the user about the initialization state of a hardware device.
The thread contains the functions that control the hardware (COM protocol). Some of them are blocking indeed. They wait for a QWaitCondition but they also have a time out less than 30 seconds (so it doesn't explain the 52 seconds).
Periodically, the hardware sends events. To catch them, I declared another thread in the hardware controller. This other thread only listens the device's events.
This way, even if the hardware controller thread is blocked, the events are caught. The listener wakes up the QWaitCondition depending on the event it receives (or the wait condition is released if the time out is reached).Maybe I should make sure all wait conditions are released when trying to stop the thread?
-
No, you don't need to create a for/while loop to use threads. In fact, creating one is a common mistake that blocks thread functions, which is why I brought it up; it's good that you don't have blocking loops.
However, if your thread is waiting for a QWaitCondition, the end result is the same as a blocking loop -- execution will stall until the wait condition is fulfilled or the wait times out. That means: until the wait condition is released, the function that initiated the wait will not return. If the function doesn't return, control doesn't return to the thread's event loop. If control doesn't return to the event loop, queued slots can't start executing.
I'm guessing that you have a 52 second delay because 2 or more "wait sessions" happen sequentially, before your function returns and the event loop gets its chance to process the "stop" signal.
Without seeing your code it's hard to recommend the optimal solution, but here's a general suggestion that works in many cases: Replace low-level synchronization primitives (mutexes, wait conditions, etc.) with Qt's high-level events. Your listener can emit signals instead of waking up QWaitConditions -- split your hardware initialization across different slots, and use your listener's signals to choose which slot to invoke. This way, your thread is never blocked, and your "stop" signal will be processed immediately.
See "this page":http://qt-project.org/wiki/Threads_Events_QObjects#2b40afeff6025af55f25a6c7ea5ab3bc for examples (pay particular attention to the "Networking / state machines" and "Jobs splittable in chunks" sections)
-
Thank you for the very good reply.
At first, I did what you recommand. That is to say two slots for each initialization step. But I found this code not very "pretty". So I moved to wait conditions.
Finally I found how to do. The hardware thread will be started by the main QMainWindow when program is launched. Then it will pass a reference of the hardware object to other sub-windows. Actually I don't think the thread has to be stopped until the applications closes. I will query the hardware all along the execution.