Design Question: How to display data collected in worker threads in the main gui thread ?
-
Hi all,
I'm currently in the design phase of an embedded application that shall be used for sending and receiving positional data. The GUI front and back end will run in the main thread. A number of subthreads will be responsible for the communication with the external transmit/receive hardware. The received data in each subthread shall be presented to the user in the GUI. The user shall also be able to transmit messages by using a message functionality in the GUI.
The update rate of received data may be different for each of the external receivers. But it will most likely not exceed 100Hz for any of the receivers.
The main design issue I'm currently facing is how to send data between the subthreads and the main (GUI) thread.
My current idea is to embed the received data in custom events and post them from the subthreads to the main thread (and the other way around when transmitting). Is there a risk of stalling the event loop in the GUI when using this approach?What is the correct "Qt way" of handling a scenario like this? E.g, to send continously updated data to / from subthreads for display in the main thread?
Best Regards,
Bubbas -
Why not use signals and slots?
-
-
Hi and thanks for your replies,
I looked at Signal/Slot across thread signalling and as you both pointed out looks like a good way of solving my problem. But now my question is: Who has ownership and is responsible for destroying a dyamically allocated custom message objcet passed (using cross thread Signal/Slot) from a workerthread to the main (gui). E.g, a worker thread calls:
MyCustomDataThing *myData = new MyCustomDataThing() and emits a signal with the myData as a parameter. The slot function in the gui receives the data and displays it on the screen.
Now to avoid memory leaks, I assume that it is the responsibility of the Slot to deallocate the received myData object. E.g. call delete myData. Am I correct or have I missed something important?Best regards,
Bubbas -
That's an interesting question. You could send the object directly, then it will be copied and destroyed correctly. If you send pointers, it's totally up to you. But take care that cross thread signals are asynchronous signals so if you delete it in the signal source after emitting the signal, you get bad pointers in the slot.
Perhaps use shared pointers in the signal so when the last reference goes away, it is deleted. Or use reference counting and self destruction. -
Hi,
By "... You could send the object directly..." do you mean allocating the object on the stack in the signalling thread?
The object then gets copied by value from the signalling thread to the gui thread thus removing the issue of memory leaks (provided the object does not contiain any pointers to dynamic allocated memory)?Best Regards,
Bubbas -
Destroying the object in the receiving slot is and only is an option if you are absolutely sure that the signal is not sent to any other slot. That's completely up to the application programmer and you cannot circumvent that anybody subscribes to your slot too.
In general, it's a bad idea to pass data in a signal as a pointer. You'd be better off by wrapping it into some class, using shared data/shared pointers or the like.
Some simple method, if the data is not too big or the class not too sophisticated, could be to serialize the data into a QByteArray, send that as argument in the signal (it's implicitly shared, no worries about unnecessary copies) and deserialize in the receiver's slot.
-
Hi,
Each data item sent between the worker threads and the gui thread will not be too big and it be wrapped in a simple message class. So using a QByteArray as you suggested seems like a good idea.
Now, a new qestions arises: for a simple message class containing a few doubles and ints, how big is the performace hit if using copy by value vs. implicit sharing (see the QByteArray suggestion above) for passing such messages (at a rate of ~80-100Hz) between threads using the Signal/Slot paradigm.
I know the question is difficult to answer, but anyone with experience in similar use cases perhaps can give a rough answer as to what method that is preferable to use.
Best Regards,
Bubbas -
If the classes are really small and contain only POD, I would send them by value, not by moving them to a byte array. You only have to register them ("see Qt quaterly 14":http://doc.qt.nokia.com/qq/qq14-metatypes.html ).
bq. Either way, to make this template code compile, we must use the Q_DECLARE_METATYPE() macro in addition to qRegisterMetaType(). We can put the macro under the class definition or in any header file:
Q_DECLARE_METATYPE(Employee)
Custom variant types are very useful in conjunction with Qt 4's model/view classes, since the model and the delegate communicate with each other through QVariants. -
Hi,
Before I decide if I shall use copy by value or go with the implicitly shared data approach I have one more question: How small is small? E.g. When is a POD data class too big to be sent by value (Qt performance vice), bigger than 128, 512, 1k? I know it's a "difficult-to-answer-question" (again) but do you have any advice?
Best regards,
Bubbas -
[quote author="Bubbas" date="1299489602"]Hi,
Before I decide if I shall use copy by value or go with the implicitly shared data approach I have one more question: How small is small? E.g. When is a POD data class too big to be sent by value (Qt performance vice), bigger than 128, 512, 1k? I know it's a "difficult-to-answer-question" (again) but do you have any advice?
Best regards,
Bubbas[/quote]Another option is to use QSharedPointer to wrap your plain C++ pointer. Using this method the QSharedPointer will delete the object when no references to it exist. So you can emit it in your worker thread, handle it in your slot in the main thread and simply forget about it. As soon as your slot goes out of scope (assuming no other slots are going to be called with it) the QSharedPointer will delete your object for you. If other slots are connected to the same signal, the object will get deleted after the last one has finished with it.
-
Hi,
Using a shared pointer is a valid choice for sure. The reference counting has a very nice "fire and forget" appeal to it.
After some thinking I've decided to use QSharedPointer for passing data messages. In the rare cases were I need to send some large amount of data in a message I'll use the QSharedDataPointer/SharedData paradigm.
Thank you all for your input and suggestions to my questions.
Best Regards,
Bubbas