Results 1 to 5 of 5

Thread: How to send a unique copy of a signal to several separate objects?

  1. #1
    Join Date
    Oct 2015
    Posts
    2
    Thanks
    3
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default How to send a unique copy of a signal to several separate objects?

    Let's say I have 100 worker objects running on 100 separate threads. At some point I'd like to send a triggerSomething signal to the doSomething slot on each of these objects. This is not that difficult. When I create each object I simply connect the triggerSomething signal to the doSomething slot on each object. Then when I emit doSomething all of the worker objects' doSomething slots will be called at the same time:

    Qt Code:
    1. // Inside some class
    2.  
    3. QPair<Worker*, QThread*> workersAndThreads;
    4.  
    5. void setupWorkers()
    6. {
    7. workersAndThreads = new QPair<Worker*, QThread*>[100];
    8. for(int i = 0; i < 100; i++)
    9. {
    10. // Create worker
    11. Worker* worker = new Worker();
    12. workersAndThreads[i].first = worker;
    13.  
    14. // Create thread
    15. QThread* thread = new QThread();
    16. workersAndThreads[i].second = thread;
    17.  
    18. // Assign worker to the thread
    19. worker->moveToThread(thread);
    20.  
    21. // Connect the worker
    22. connect(this, SIGNAL(triggerSomething()), worker, SLOT(doSomething()));
    23.  
    24. // Start the thread (note that the worker isn't doing anything yet).
    25. thread->start();
    26. }
    27. }
    28.  
    29. void makeAllTheWorkersDoSomethingAtTheSameTime()
    30. {
    31. emit triggerSomething();
    32. }
    To copy to clipboard, switch view to plain text mode 

    Unfortunately, if I need all of the worker objects to do something at different points in time then this won't work. The triggerSomething signal is hooked up to all of the workers, so there's no way to emit the signal and have it trigger only one of these workers. I can think of two horrible work-arounds for this. First, we could pass an ID in the signal and have each worker object check its ID in the slot to make sure that the signal was really for it. If it was, then it would do something, and if it wasn't then it would disregard the signal. This is very wasteful though since, if there are 100 separate emits (for the 100 objects), there end up being 9,900 unnecessary calls to slots (for the objects whose IDs didn't match the signal). A somewhat better approach which I'm inclined to use right now is this:

    Qt Code:
    1. void makeASpecificWorkerDoSomething(int indexOfSpecificWorker)
    2. {
    3. // disconnect this object from all previous triggerSomething connections since we don't want to call them all at once.
    4. disconnect(this, SIGNAL(triggerSomething()), 0, 0)
    5.  
    6. // connect this object to the specific Worker object that we want to call.
    7. Worker* specificWorker = workersAndThreads[indexOfSpecificWorker].first;
    8. connect(this, SIGNAL(triggerSomething()), specificWorker, SLOT(doSomething())
    9.  
    10. // signal the specific worker object
    11. emit triggerSomething();
    12. }
    To copy to clipboard, switch view to plain text mode 

    This accomplishes what I want, but unfortunately it requires a disconnect and a connect every time I want to emit a signal. I'm not sure how costly the connect/disconnect operations are, but I'm inclined to believe that there's a better way to do this.

    Any ideas?
    Last edited by nthexwn; 12th January 2016 at 01:15.

  2. #2
    Join Date
    Mar 2008
    Location
    Kraków, Poland
    Posts
    1,536
    Thanked 284 Times in 279 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: How to send a unique copy of a signal to several separate objects?

    Do not use signals only QMetaObject::invokeMethod.

  3. The following user says thank you to Lesiok for this useful post:

    nthexwn (12th January 2016)

  4. #3
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: How to send a unique copy of a signal to several separate objects?

    Lesiok's solution clearly is the way to go with your current infrastructure.

    However, allocating a QThread for each worker is expensive. Do you really need the workers to run in separate (and persistent) threads, or do you only need them to run outside the main (GUI?) thread, in as many threads as the system can effectively run concurrently? In the latter case, consider using QtConcurrent::run().

  5. The following user says thank you to yeye_olive for this useful post:

    nthexwn (12th January 2016)

  6. #4
    Join Date
    Oct 2015
    Posts
    2
    Thanks
    3
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: How to send a unique copy of a signal to several separate objects?

    Quote Originally Posted by Lesiok View Post
    Do not use signals only QMetaObject::invokeMethod.
    So if I'm understanding this correctly, I want something like this instead?

    Qt Code:
    1. void makeASpecificWorkerDoSomething(int indexOfSpecificWorker)
    2. {
    3. // Look up worker
    4. Worker* specificWorker = workersAndThreads[indexOfSpecificWorker].first;
    5.  
    6. // Schedule worker to do something in its own event loop
    7. QMetaObject::invokeMethod(worker, SLOT(doSomething()), Qt::QueuedConnection);
    8. }
    To copy to clipboard, switch view to plain text mode 

    Quote Originally Posted by yeye_olive View Post
    Do you really need the workers to run in separate (and persistent) threads, or do you only need them to run outside the main (GUI?) thread, in as many threads as the system can effectively run concurrently? In the latter case, consider using QtConcurrent::run().
    I believe I do need them to run in their own persistent threads. Each thread is running a separate copy of a game. Each game uses the event loop on that thread to interface with an AI (or player) either directly (compiled into the program) or through the network. As far as I can tell QtConcurrent just launches a single function per thread and doesn't provide event loops. Is that correct?

    I suppose I could re-write the game to use its own manually written loop inside a main function instead of relying on the event loop provided by QThread. Do you believe this would be a better approach?

  7. #5
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: How to send a unique copy of a signal to several separate objects?

    Quote Originally Posted by nthexwn View Post
    So if I'm understanding this correctly, I want something like this instead?

    Qt Code:
    1. void makeASpecificWorkerDoSomething(int indexOfSpecificWorker)
    2. {
    3. // Look up worker
    4. Worker* specificWorker = workersAndThreads[indexOfSpecificWorker].first;
    5.  
    6. // Schedule worker to do something in its own event loop
    7. QMetaObject::invokeMethod(worker, SLOT(doSomething()), Qt::QueuedConnection);
    8. }
    To copy to clipboard, switch view to plain text mode 
    Almost. As the documentation for QMetaObject::invokeMethod() specifies: You only need to pass the name of the signal or slot to this function, not the entire signature. Which yields:
    Qt Code:
    1. QMetaObject::invokeMethod(worker, "doSomething", Qt::QueuedConnection);
    To copy to clipboard, switch view to plain text mode 
    Notice that you could omit the argument Qt::QueuedConnection, because doing so uses the default Qt::AutoConnection, which behaves like Qt::QueuedConnection in this case (worker does not live in the thread executing QMetaObject::invokeMethod()).

    Quote Originally Posted by nthexwn View Post
    I believe I do need them to run in their own persistent threads. Each thread is running a separate copy of a game. Each game uses the event loop on that thread to interface with an AI (or player) either directly (compiled into the program) or through the network. As far as I can tell QtConcurrent just launches a single function per thread and doesn't provide event loops. Is that correct?
    That is correct, QtConcurrent::run() only schedules a function to be run in another thread taken from a thread pool once one becomes available. Since you need event loops, your current solution looks reasonable. If, during the optimization phase of your project, you realize that too many threads remain idle often enough, then you could try to run several instances of the game in each thread.

    Quote Originally Posted by nthexwn View Post
    I suppose I could re-write the game to use its own manually written loop inside a main function instead of relying on the event loop provided by QThread. Do you believe this would be a better approach?
    Probably not, unless performance becomes an issue and you can measurably improve it by tailoring the event loop to your needs. Your current approach seems well adapted to your use case at this stage.

  8. The following user says thank you to yeye_olive for this useful post:

    nthexwn (14th January 2016)

Similar Threads

  1. Replies: 1
    Last Post: 10th December 2014, 07:00
  2. live time of objects send via signals
    By tuli in forum Qt Programming
    Replies: 7
    Last Post: 28th November 2012, 09:36
  3. QDBus : Send an array of objects (C++)
    By lordjoseph in forum Qt Programming
    Replies: 6
    Last Post: 26th January 2012, 11:22
  4. Replies: 7
    Last Post: 7th June 2010, 14:13
  5. Replies: 4
    Last Post: 2nd December 2008, 16:44

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.