Signal/slot across threads - remove duplicate events in event loop
-
wrote on 7 Nov 2011, 16:42 last edited by
Hello,
I have several signal/slot connections between a worker thread and the GUI thread. Using the default delivery method, which posts an event in the GUI's event queue, one of my connections can occasionally emit signals faster into the event queue than the slot being called. So what happens is multiple signals are being queue that are just repeats and the queue gets backed up. Is there a method to ensure that only one signal (event) is in the queue at a time?
Thanks!
-
wrote on 7 Nov 2011, 17:25 last edited by
As far as I know, there is no such method.
I think it would be best the throttle the number of messages you are sending at the source. The best performance of multi-threaded applications can be reached if you communicate as little as possible between threads. They should be as independent as possible. Do you really need to send updates to the GUI thread that fast?In the past, I have throttled the delivery of update messages from a worker to the GUI thread to at most two per second, and that worked very well for me.
-
wrote on 7 Nov 2011, 17:52 last edited by
Read this topics in help, maybe:
QCoreApplication::hasPendingEvents ()
QCoreApplication::removePostedEvents ( QObject * receiver, int eventType ) -
wrote on 7 Nov 2011, 19:50 last edited by
I did look at the QCoreApplication::removePostedEvents ( QObject * receiver, int eventType ) method, but I am not sure what the eventType would be for a signal/slot.
-
wrote on 7 Nov 2011, 20:03 last edited by
After looking through the source, the eventType is: MetaCall (value 43 in 4.6.3)
-
wrote on 8 Nov 2011, 01:24 last edited by
To use QCoreApplication::removePostedEvents(), you could create a custom event. Then the event type would be known.
You may also consider communicating through an atomic flag. The receiver could then poll when it is able to handle information from the worker thread.
Of course neither of these methods are as convenient as signals/slots.
-
wrote on 8 Nov 2011, 07:39 last edited by
I think that if you have to deliver so much events as so fast rates you have to build your own event queue, that will notify (via signals) your GUI thread once at a time and the GUI thread will process (consume) all of the events in the queue. So the queue will work as a concentrator for the incoming events: the sender thread will post events, the queue will store and present them in a block to the GUI thread when this is signalled.
-
wrote on 8 Nov 2011, 08:06 last edited by
Perhaps you can use the technique I described "here":http://developer.qt.nokia.com/wiki/Delay_action_to_wait_for_user_interaction to limit the number of signals going down the event queue. Interesting to see that it actually is possible to manipulate the contents of the eventqueue.
-
wrote on 16 Feb 2023, 09:39 last edited by ScumCoder
Allow me to revive this topic, since more than a decade later it is still as relevant as ever.
@andre said in Signal/slot across threads - remove duplicate events in event loop:
In the past, I have throttled the delivery of update messages from a worker to the GUI thread to at most two per second, and that worked very well for me.
This isn't a proper solution, because it relies on a wild guess about the time needed for the consumer to consume. One day you'll have your code run on a machine on which the GUI thread takes more than 0.5 s to process a request, and boom, unbounded queue growth.
If the receiver is the GUI thread (and thus the events that you send there are just to refresh the GUI), the proper way is to have the GUI thread reply to the worker thread with ACKs.
In the worker class:- Have a boolean variable called "SignalRequested" or something like that. It doesn't even have to be atomic.
- Have a slot that will assert this boolean, called "RequestSignal" or something like that.
- Inside the producer loop, check this boolean, and if it's true, reset it back to false and emit the signal.
In the GUI class:
- Invoke the RequestSignal slot on the worker during startup.
- Wherever you receive the signal from the worker, refresh the UI and start a single-shot timer connected to the RequestSignal slot on the worker. The timer's interval will determine the GUI's refresh rate.
There you go, an effective solution that'll guarantee at most a single undelivered signal at all times.
If the receiver is not the GUI thread, the only proper way is to make your own queue (at least until Qt provides us with means to control its internal one).
- Write a RingBuffer class (check Wikipedia for examples).
- Instantiate it with the type of your event.
- Protect this instance with a mutex.
- Add a WaitCondition.
- Implement the classic producer/consumer pattern.
Voila: now, in the Producer thread, you can actually assess the situation whenever you are going to add an event to the queue and do some meaningful things if the queue grows too much - issue a warning, drop the event, etc., instead of blindly shoving events into the queue and hoping for the best.