[solved] Slot invoked in context of wrong thread !?
-
Hi.
I have the following situation: I have a GUI application that creates a "background" thread. The thread class is derived from QThread. Inside this "background" thread, a large number of sub-tasks need to be executed. These tasks, derived from QRunable, are created and added to a QThreadPool. This all happens inside the "background" thread, the Main/GUI thread is not involved at all here.
Now, I also need the "background" thread to get a Signal when one of the tasks has finished (in which case it will either start the next tasks or exit). So the thread class has a Slot "taskDone", which will be connected, via a queued connection, to each task object before the task is started. And the task object will emit the Signal once it is done. The "background" thread also runs an event loop, i.e. it calls exec() after the first few tasks have been created/started. All seems straight forward.
Now, I have noticed that the "taskDone" slot is actually invoked in the context of the Main/GUI thread, rather than the "background" thread! So can somebody explain why that is? To my understand, the purpose of a "queued" connection is that the slot will be invoked in the context of the thread to which the receiving object belongs - which clearly would be the "background" thread in this case...
[quote]Qt::QueuedConnection
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.[/quote]
Thanks!
-
An object derived from QThread does not live in the thread it manages. It's just its run method is executed in separate thread when you call start. The object itself still lives in the thread it was created in, which is the main thread in your case.
That is why there's moveToThread() and why deriving from QThread is not the recommended way of doing multithreading.
-
[quote author="Chris Kawa" date="1406990551"]An object derived from QThread does not live in the thread it manages. [...] The object itself still lives in the thread it was created in, which is the main thread in your case.[/quote]
I see. That makes sense. Especially when considering that, at the time when the QThread object is created, the thread it manages doesn't even exist yet.
And moveToThread() fixed my problem, indeed.
Thanks!
-
Great.
I just hope you didn't do moveToThread(this) ;) -
That's exactly what was needed.
-
"You're doing it wrong":http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
-
Hi MuldeR, here's what the "official documentation":http://qt-project.org/doc/qt-5/qthread.html says (emphasis added):
[quote]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.[/quote]
-
bq. My #1 biggest gripe with this code is moveToThread(this); I see so many people using this without understanding what it does. What does it do, you ask? The moveToThread() function tells Qt to ensure that event handlers, and by extension signals and slots, are called from the specified thread context.
Since I do understand what it does and since that's exactly what I want/need here, I'm not doing it wrong ;-)
I understand that I could create a separate worker object. But I don't see how having to maintain a separate worker object benefits my project. The argument for this approach seem to be that inheriting from QThread is evil, because "it’s confusing" and "not how QThread was designed to be used". The former is quite subjective and, personally, I can't agree. The latter is a bit unspecific. So what exactly would be the real drawback for my project?
-
[quote author="MuldeR" date="1407079364"]Since I do understand what it does and since that's exactly what I want/need here, I'm not doing it wrong ;-)
I understand that I could create a separate worker object. But I don't see how having to maintain a separate worker object benefits my project. The argument for this approach seem to be that inheriting from QThread is evil, because "it’s confusing" and "not how QThread was designed to be used". The former is quite subjective and, personally, I can't agree. The latter is a bit unspecific. So what exactly would be the real drawback for my project?[/quote]I suppose there's a subtle difference between "wrong" and "anti-pattern".
If you understand the mechanisms, then feel free to deviate from established patterns where you find appropriate. After all, you are the designer for your project. There is no drawback to your project.
However, do take time to understand why the (anti-)pattern exists in the first first place. In this case, it was very common for users to shoot themselves in the foot when adding slots to QThread subclasses. That's why it's considered an anti-pattern.
The Qt documentation is now very clear: If you want slots to be invoked in the context of a new thread, use a worker object. If you want to run code in another thread without don't need an event loop, subclass QThread.
Anyway, how much difference is there between maintaining a worker object vs. maintaining your custom QThread?
-
[quote author="JKSH" date="1408365353"]However, do take time to understand why the (anti-)pattern exists in the first first place. In this case, it was very common for users to shoot themselves in the foot when adding slots to QThread subclasses. That's why it's considered an anti-pattern.[/quote]
I only find it "natural" that if a QThread creates internal objects inside it's run() method and connects to these objects (with queued connection), then the corresponding slots will be invoked in the context of the thread that is controlled by the QThread object and that is executing the run() method - rather than in the context of the "main" thread, which may be in whatever state that we don't know and don't want to care about at this point...
[quote author="JKSH" date="1408365353"]Anyway, how much difference is there between maintaining a worker object vs. maintaining your custom QThread?[/quote]
Well, it means that I will now have to maintain two separate objects, the QThread and the Worker object - for something that logically, i.e. from the caller's perspective, should be a single "background task" object. Of course we don't want to the caller having to create two separate objects and having to connect them in a certain way in order to get everything working as expected. Actually, we don't want the caller having to worry about such "details" at all. So, in the end, I would probably end up wrapping the QThread object and its Worker object into yet another "high level" class that provides a clean interface to the caller and hides the implementation details. That's all very doable! But still adds a significant amount of extra complexity - which I don't want to add unless it gives a real benefit (or avoids a real problem)...