简体   繁体   中英

Emitting a signal to one object in a set dynamically

I have a situation where I have a single Emitter object and a set of Receivers. The receivers are of the same class, and actually represent a set of devices of the same type. I'm using the Qt framework.

  • The Emitter itself first gets a signal asking for information from one of the devices.

  • In the corresponding slot, the Emitter has to check to see which of the Receivers are 'ready', and then send its own signal to request data to one of the devices (whichever is ready first).

The Emitter receives signals very quickly, on the order of milliseconds. There are three ways I can think of safely requesting data from only one of the devices (the devices live in their own threads, so I need a thread-safe mechanism). The number of devices isn't static, and can change. The total number of devices is quite small (definitely under 5-6).

1) Connect to all the devices when they are added or removed. Emit the one request and have the devices objects themselves filter out whether the request is for them using some specific device tag. This method is nice because the request slot where the check occurs will execute in a dedicated thread's context, but wasteful as the number of devices go up.

2) Connect and disconnect from the object within the Emitter on the fly when it's necessary to send a request.

3) Use QMetaObject::invokeMethod() when its necessary to send a request.

Performance is important. Does anyone know which method is the 'best', or if there's a better one altogether?

Regards

Pris

Note: To clarify: Emitter gets a signal from the application, to get info by querying the device. Crazy ASCII art go:

(app)<---->(emitter)<------>(receivers)<--|-->physical devices

I'm amusing here this is multi thread environment.

If you are restricted to Qt signal / slot system between then the answer for your specific questions:

1) is definitely not the way to go. On an emit from the Emitter a total number of events equal to the number of Receivers will be queued for the thread(s) event loops of the devices, then the same number of slot calls will occur once the thread(s) reach those event. Even if most of the lost just if(id!=m_id) return; on their first line, its a significant amount of things going on in the core of Qt. Place a breakpoint in one of your slots that is evoked by a Qt::QueuedConnection signal and validate this looking at the actual stack trace. Its usually at least 4 call deep from the xyEventLoop::processEvents(...) , so "just returning" is definitely not "free" in terms of time.

2) Not sure how Qt's inner implementation actually is, but from what I know connecting and disconnecting most likely include inserting and removing the sender and receiver into some lists, which are most likely accessed with QMutex locking. - might also be "expensive" time-wise, and rapidly connecting and disconnecting is definitely not a best practice.

3) Probably the least "expensive time-wise" solution you can find that is still using Qt's singnal-slot system.

optionally) Take a look at QSignalMapper . It is designed exactly for what you planned to do in option 1).

There are more optimal solutions to communicate between your Emitter and Receivers , but as a best practice I'd first choose the option that is most easy to use and fast to implement, yet has a chance of being fast enough run-time (that is option 3). ). Then, when its done, see if it meets your performance requirements. If it does not, and only then, consider using shared memory with mutexes in a data provider - data consumer architecture ( Emitter thread rapidly post request data in a circular list, while the Receiver thread(s) reads them whenever have time, then post results back a similar way, while the Emitter thread constantly polls for done results.)

Based on the information you have provided I would still recommend a Reactor implementation. If you don't use ACE then you can implement your own. The basic architecture is as follows:

  1. use select to wake up when signal or data is received from the App.
  2. If there is a socket ready on the sending list then you just pick one and send it data
  3. When data is sent the Receiver removes itself from the set of sockets/handlers that are available
  4. When data is processed the Reciever re-registers itself to the list of available recipients.

The reason I suggested ACE is because it has one of the simplest to use implementations of the Reactor pattern.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM