Results 1 to 14 of 14

Thread: QTimer / QThread question

  1. #1
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default QTimer / QThread question

    I have an application that sorts arrays of ints (6 at a time). There is a main QWidget that has 6 subclassed QWidgets that handle the drawing of the sort progress. There is also another thread that handles all the signals from buttons and sliders. This one creates the 6 sort threads and also a "ThreadWatcher" thread. This last one calls update() for the corrosponding QWidget while the sort threads are alive and updates QLabels after the sort threads exit. Originally I just had a loop that checked each one and called update() and then slept for 10ms (which worked), but I decided to try to do it all with slots.

    The code below gives the following errors:

    "QObject: Cannot create children for a parent that is in a different thread. (Parent is ThreadWatcher2(0x221b888), parent's thread is QThread(0x3f4ae0), current thread is ThreadWatcher2(0x221b888)" -- on start but this still works fine.

    "QObject::killlTimer: timers cannot be stopped from another thread" -- when I try to pause, this doesn't work.

    Attempting to kill (setting control[1]) doesn't work (here, on the loop-based method it did), but I can probably figure that one out.

    I'll leave out everything before run(), so here's the revelant variables. I also left out the other 5 finished() handlers. If there's some way to create a single handler with an (int) to differentiate which I'd be interested in that as well, as they are all identical except the array index. I'm guessing QOBject::moveToThread() is the solution (if there is one) but I'm unclear on the specifics. updateTimer->moveToThread(this) after allocation in run() doesn't seem to help.

    alive[6] = bool array, marked to false in finished() handler for each thread
    window[6] = references to the QWidgets to call update() on
    thread[6] = array of sort thread references
    control[2] = volatile bool array, [0] for pause and [1] for kill, set in (ommitted) parent thread
    threadsLeft = starts a 6, decremented in each finished() handler
    lock / condition = QMutex / QWaitCondition set by parent to pause

    Qt Code:
    1. void ThreadWatcher2::run() {
    2. updateTimer = new QTimer(this);
    3. connect(updateTimer, SIGNAL(timeout()), this, SLOT(timeoutHandler()));
    4. connect(thread[0], SIGNAL(finished()), this, SLOT(finishedHandler0()));
    5. connect(thread[1], SIGNAL(finished()), this, SLOT(finishedHandler1()));
    6. connect(thread[2], SIGNAL(finished()), this, SLOT(finishedHandler2()));
    7. connect(thread[3], SIGNAL(finished()), this, SLOT(finishedHandler3()));
    8. connect(thread[4], SIGNAL(finished()), this, SLOT(finishedHandler4()));
    9. connect(thread[5], SIGNAL(finished()), this, SLOT(finishedHandler5()));
    10. updateTimer->start(delay);
    11.  
    12. QThread::exec();
    13. status->setText("Status: Done");
    14. }
    15. void ThreadWatcher2::timeoutHandler() {
    16. for (int i=0; i<6; i++) {
    17. if (alive[i])
    18. window[i]->update(); }
    19.  
    20. if (threadsLeft==0) {
    21. QThread::exit(0); }
    22.  
    23. if (control[1])
    24. QThread::exit(1);
    25. else if (control[0]) {
    26. updateTimer->stop();
    27. lock->lock();
    28. condition->wait(lock);
    29. lock->unlock();
    30. updateTimer->start(delay);}
    31. }
    32. void ThreadWatcher2::finishedHandler0() {
    33. disconnect(SIGNAL(finished()), this, SLOT(finishedHandler0()));
    34. alive[0] = false;
    35. threadsLeft--;
    36. seconds[0] = (float)runTime[0] / 1000.0f;
    37. time[0]->setText("Time: "+QString::number(seconds[0],'g',3));
    38. }
    39. void ThreadWatcher2::finishedHandler1() {
    40. .
    41. .
    42. .
    To copy to clipboard, switch view to plain text mode 

    thanks
    Last edited by Galen; 14th April 2010 at 00:59. Reason: more info

  2. #2
    Join Date
    Feb 2007
    Location
    Karlsruhe, Germany
    Posts
    469
    Thanks
    17
    Thanked 90 Times in 88 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: QTimer / QThread question

    Hi Galen!

    1. You should have a look at QtConcurrent and the qt concurrent examples. That would save you a lot of effort!

    2. instead of your 6 finishedHandlers look at QSignalMapper.

    3.
    Qt Code:
    1. void ThreadWatcher2::finishedHandler0() {
    2. disconnect(SIGNAL(finished()), this, SLOT(finishedHandler0()));
    To copy to clipboard, switch view to plain text mode 
    There is no need here, to manually disconnect this. Finished is only called once from the thread.
    And signal/slots are automatically disconnected as soon as one of the connected
    objects is destroyed.

    4. As for your QObject Warning. It says, what you did wrong. Do it otherwise :-> If you can't find it, you need to provide your setup code..

    HIH

    Johannes

  3. #3
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Thanks for the info, QSignalMapper does look like the solution to that issue, I will give it a try.

    I definately want OS-based threads as this is a code sample based around that. I did find that I could get around all of this by creating the timer in the parent class and just stopping it there in the pause button handler. Accessing the control array in the threadwatcher handler just didn't seem to want to work right (is there something special about the scope from within a slot as opposed to an average function?) so I guess its just as well. I'm not sure how much having a 10ms timer taxes the parent (if at all) if the signals are all going to a child thread. The signal is actually connected in the child thread if that makes any difference.

    One more question... I'm confused whether I should be choosing Qt:irectConnection or Qt::QueuedConnection with these slots. Is there something potentially unsafe about direct connections? What happens if the thread is executing another slot when the signal is delivered?

    thanks

  4. #4
    Join Date
    Feb 2007
    Location
    Karlsruhe, Germany
    Posts
    469
    Thanks
    17
    Thanked 90 Times in 88 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: QTimer / QThread question

    1. Don't specify the connection type! Qt choses it well! Direct-Connection slots are directly executed in the context of the calling thread, whereas queued-connections are queued into the destination threads message queue and executed when they are retrieved by that destination thread. If you don't specify anything, qt choses automatically. If source and destination reside in different threads queuedconnection is used, otherwise direct.

    2. QtConcurrent spawns threads.

    3. I don't get what you are doing with your timers.

    Johannes

  5. #5
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Thanks, I'll take a look, though It's working well now.. just not the way I have it above.

    Well, basically I want the screen to refresh every 10 ms. So I made a threadwatcher class with a 10 ms timer. I decided I wanted to seperate it from the button handlers and such, so I made it an independent thread that creates a QTimer. Starting the timer there complained but still worked. The only thing is that you can pause the entire application. I suppose I could just let it keep refreshing the screen anyway but I'd rather not. So when the pause button is pressed, the timer needs to be stopped or interrupted somehow. The puse button handler is in the parent thread. With the sort threads, and interrupt is requested by the parent changing a shared variable (control[0], which is control[i][0] in the parent, with i being the thread number). In this code sample, that same mechanism doesn't work. I have no idea why, but changes to the variable don't propograte to the timeout slot. They do get to run(), but in the slot function they're always false. Also even if that would work I would need to stop the timer from within the timeout slot to pause it, and that doesn't seem to want to work either. I can see from the "Similar Threads" below that using QTimers in threads is problematic, so maybe I should just be glad I found another way to make it work even if it seems a bit clunkier (parent controls timer externally).

    There really isn't anything more to the code for this class. The construtor sets lots of variables, but they are all JLabel references and the like. The timer is a member variable reference and the actual object is created in run().

  6. #6
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Looks like new QTimer() instead of new QTimer(this) takes care of the error message, but it seems to consider the slot a different thread, so that doesn't help too much with stopping the timer from within the slot.
    Last edited by Galen; 15th April 2010 at 05:08.

  7. #7
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Well, here's another problem. When all delay is removed the sorting is instantaneous. In this case 1-2 of the signals dissapear. The commented out portion uses the QSignalMapper, which has the same problem. I tried the Qt::QueuedConnection option but no help. Any ideas? I'd guess you can't max out the event loop so easily.

    Qt Code:
    1. void ThreadWatcher2::run() {
    2. /* QSignalMapper* signalMapper = new QSignalMapper();
    3.   for (int i=0; i<6; i++) {
    4.   connect(thread[i], SIGNAL(finished()), signalMapper, SLOT(map()));
    5.   signalMapper->setMapping(thread[i], i); }
    6.   connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(finishedHandler(int)));
    7. */
    8. connect(updateTimer, SIGNAL(timeout()), this, SLOT(timeoutHandler()));
    9. connect(thread[0], SIGNAL(finished()), this, SLOT(finishedHandler0()));
    10. connect(thread[1], SIGNAL(finished()), this, SLOT(finishedHandler1()));
    11. connect(thread[2], SIGNAL(finished()), this, SLOT(finishedHandler2()));
    12. connect(thread[3], SIGNAL(finished()), this, SLOT(finishedHandler3()));
    13. connect(thread[4], SIGNAL(finished()), this, SLOT(finishedHandler4()));
    14. connect(thread[5], SIGNAL(finished()), this, SLOT(finishedHandler5()));
    15.  
    16. QThread::exec();
    17. }
    18. void ThreadWatcher2::timeoutHandler() {
    19. for (int i=0; i<6; i++)
    20. window[i]->update();
    21.  
    22. if (threadsLeft==0)
    23. QThread::exit(0);
    24. }
    25. void ThreadWatcher2::finishedHandler(int i) {
    26. updateLabels(i);
    27. }
    28. void ThreadWatcher2::finishedHandler0() {
    29. updateLabels(0);
    30. }
    31. void ThreadWatcher2::finishedHandler1() {
    32. updateLabels(1);
    33. }
    34. void ThreadWatcher2::finishedHandler2() {
    35. updateLabels(2);
    36. }
    37. void ThreadWatcher2::finishedHandler3() {
    38. updateLabels(3);
    39. }
    40. void ThreadWatcher2::finishedHandler4() {
    41. updateLabels(4);
    42. }
    43. void ThreadWatcher2::finishedHandler5() {
    44. updateLabels(5);
    45. }
    46. inline void ThreadWatcher2::updateLabels(int t) {
    47. alive[t] = false;
    48. threadsLeft--;
    49. seconds[t] = (float)runTime[t] / 1000.0f;
    50. if (seconds[t] == 0.0f)
    51. seconds[t] = 0.001f;
    52. time[t]->setText("Time: "+QString::number(seconds[t],'g',3));
    53.  
    54. if (isFirst) {
    55. isFirst = false;
    56. long minTime = runTime[t];
    57. min = t;
    58. long x;
    59.  
    60. for (int k=0; k<6; k++) {
    61. x = runTime[k];
    62. if ((x > -1) && (x < minTime)) {
    63. minTime = x;
    64. min = k; } }
    65.  
    66. if (min != t) {
    67. seconds[min] = (float)runTime[min] / 1000.0f;
    68. if (seconds[min] == 0.0f)
    69. seconds[min] = 0.001f; }
    70.  
    71. if (min == t)
    72. ratio[t]->setText("Ratio: 1x");
    73. else {
    74. ratio[t]->setText("Ratio: "+QString::number(seconds[t]/seconds[min],'g',3)+"x"); } }
    75. else
    76. ratio[t]->setText("Ratio: "+QString::number(seconds[t]/seconds[min],'g',3)+"x");
    77. }
    To copy to clipboard, switch view to plain text mode 
    thanks
    Last edited by Galen; 15th April 2010 at 07:49.

  8. #8
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Duh, nevermind, I still had the sorters starting before the threadwatcher, reversing that fixed it.

  9. #9
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Apparently moving the thread object to the thread was what I really needed to do, otherwise the slots run in the parent's thread.

    Qt Code:
    1. threadWatcher->moveToThread(threadWatcher);
    2. threadWatcher->start(QThread::NormalPriority);
    To copy to clipboard, switch view to plain text mode 
    I'm still a little unclear how bad making function calls to gui objects (like QLabels) from another thread is. Seems to work fine, but I guess I can always use signals or make mutex-protected mutator functions in the gui thread instead.

  10. #10
    Join Date
    Sep 2009
    Location
    UK
    Posts
    2,447
    Thanks
    6
    Thanked 348 Times in 333 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    I put the "moveToThread" call in the threads constructor, that way I don't forget the call if I create multiple copies of the class.

  11. #11
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTimer / QThread question

    Quote Originally Posted by Galen View Post
    I'm still a little unclear how bad making function calls to gui objects (like QLabels) from another thread is.
    It's VeryBad(TM).

    Seems to work fine,
    Then maybe the calls are not made from worker threads after all.
    but I guess I can always use signals or make mutex-protected mutator functions in the gui thread instead.
    Hard to say without knowing what you are trying to do but I can tell you one thing - you won't be able to protect widgets with mutexes.

  12. #12
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Thanks. Well, "VeryBad(TM)" sounds bad, so I guess I'll just emit signals with a QString argument to the gui instead. What I meant was make methods in the main qwdiget to change the contents and lock a mutex at the beginning of it and unlock at the end. That would protect everything inside in a rather crude way, and as long as that is the only method that changes those objects that would be sufficient, right? But then again, this still involves function calls to the gui from another thread. Is this inherently problematic?

    I think the slots must be running in the threadwatcher thread. Here's QThread::currentThread() at various points.

    main 0x6f51a8
    sw 0x6f51a8
    sorter 0x6f51a8
    tw-r 0x2222e40
    tw-h 0x2222e40

    Main creates a SortWindow (extends QWidget) and a Sorter (extends QObject) object. The latter has handlers for the buttons and such (I actually tried to make this a thread for awhile but realized this wasn't a great idea). The start button handler launched 6x sort threads and the threadWatcher thread. Tw-r is sampled in run() and Tw-h is in the finishedHandler slot. Pretty conclusive, no?

    Qt Code:
    1. void ThreadWatcher::run() {
    2. QTextStream cout(stdout, QIODevice::WriteOnly);
    3. cout << "tw-r " << QThread::currentThread() << endl;
    4. QSignalMapper threadMapper;
    5. updateTimer = new QTimer();
    6.  
    7. for (int i = 0; i < 6; i++) {
    8. connect(thread[i], SIGNAL(finished()), &threadMapper, SLOT(map()));
    9. threadMapper.setMapping(thread[i], i);}
    10.  
    11. connect(&threadMapper, SIGNAL(mapped(int)), this, SLOT(finishedHandler(int)));
    12. connect(updateTimer, SIGNAL(timeout()), this, SLOT(timeoutHandler()));
    13.  
    14. connect(this, SIGNAL(refresh(int)), window, SLOT(draw(int)));
    15.  
    16. updateTimer->start(timerVal);
    17.  
    18. QThread::exec();
    19.  
    20. delete updateTimer;
    21. }
    22. void ThreadWatcher::timeoutHandler() {
    23.  
    24. for(int i=0; i<6; i++) {
    25. if (alive[i])
    26. emit refresh(i); }
    27.  
    28. if ((threadsLeft==0) || (control[1])) {
    29. QThread::exit(0); }
    30.  
    31. else if (control[0]) {
    32. lock->lock();
    33. updateTimer->stop();
    34. condition->wait(lock);
    35. lock->unlock();
    36. updateTimer->start(timerVal); }
    37. }
    38. void ThreadWatcher::finishedHandler(int t) {
    39. QTextStream cout(stdout, QIODevice::WriteOnly);
    40. cout << "tw-h " << QThread::currentThread() << endl;
    41. emit refresh(t);
    42. alive[t] = false;
    43. threadsLeft--;
    44.  
    45. seconds[t] = (float)runTime[t] / 1000.0f;
    46. if (seconds[t] == 0.0f)
    47. seconds[t] = 0.001f;
    48. time[t]->setText("Time: "+QString::number(seconds[t],'g',3));
    49. if (isFirst) {
    50. isFirst = false;
    51. long minTime = runTime[t];
    52. min = t;
    53. long x;
    54.  
    55. for (int k=0; k<6; k++) {interval)
    56. x = runTime[k];
    57. if ((x > -1) && (x < minTime)) {
    58. minTime = x;
    59. min = k; } }
    60.  
    61. if (min != t) {
    62. seconds[min] = (float)runTime[min] / 1000.0f;
    63. if (seconds[min] == 0.0f)
    64. seconds[min] = 0.001f; }
    65.  
    66. if (min == t)
    67. ratio[t]->setText("Ratio: 1x");
    68. else {
    69. ratio[t]->setText("Ratio: "+QString::number(seconds[t]/seconds[min],'g',3)+"x"); } }
    70.  
    71. else
    72. ratio[t]->setText("Ratio: "+QString::number(seconds[t]/seconds[min],'g',3)+"x");
    73. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by Galen; 19th April 2010 at 23:04.

  13. #13
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTimer / QThread question

    Quote Originally Posted by Galen View Post
    What I meant was make methods in the main qwdiget to change the contents and lock a mutex at the beginning of it and unlock at the end. That would protect everything inside in a rather crude way, and as long as that is the only method that changes those objects that would be sufficient, right?
    Unfortunately not. This protects code, not the shared resource. Qt will still access the widget from within other methods "behind your back" and you can't protect that.

    But then again, this still involves function calls to the gui from another thread. Is this inherently problematic?
    Yes, GUI resources just can't be protected from within application code.

    I think the slots must be running in the threadwatcher thread. Here's QThread::currentThread() at various points.
    The thread-watcher thread is redundant - all it does is to monitor other threads so it can be done from the main thread.

    Take a look at QtConcurrent and especially QFutureWatcher, by the way.

  14. #14
    Join Date
    Apr 2010
    Posts
    17
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer / QThread question

    Well, true, but its my redundant thread and I love it. Plus I want to use emit in this code somewhere. So now I've stripped out all the function calls. The emit signal system is really pretty much the same thing so no biggie. Everything seems to be running pretty well now, the inline function at the bottom fixes a problem whereby threads with longer running time somehow end up signalling first.

    Thanks, I'll take a look at those classes, but this isn't even a real app so I probably won't be switching over even if it would run better. For this code sample I'm pretty much assuming an unlimited number of cores.


    Qt Code:
    1. void ThreadWatcher::run() {
    2. QSignalMapper threadMapper;
    3. updateTimer = new QTimer();
    4.  
    5. for (int i = 0; i < 6; i++) {
    6. connect(thread[i], SIGNAL(finished()), &threadMapper, SLOT(map()));
    7. threadMapper.setMapping(thread[i], i); }
    8.  
    9. connect(&threadMapper, SIGNAL(mapped(int)), this, SLOT(finishedHandler(int)));
    10. connect(updateTimer, SIGNAL(timeout()), this, SLOT(timeoutHandler()));
    11. connect(this, SIGNAL(refresh(int)), window, SLOT(draw(int)));
    12. connect(this, SIGNAL(time(int, QString)), window, SLOT(setTimeLabel(int, QString)));
    13. connect(this, SIGNAL(ratio(int, QString)), window, SLOT(setRatioLabel(int, QString)));
    14.  
    15. updateTimer->start(timerVal);
    16.  
    17. QThread::exec();
    18.  
    19. delete updateTimer;
    20. }
    21. void ThreadWatcher::timeoutHandler() {
    22.  
    23. for(int i=0; i<6; i++) {
    24. if (alive[i])
    25. emit refresh(i); }
    26.  
    27. if ((threadsLeft==0) || (control[1])) {
    28. QThread::exit(0); }
    29.  
    30. else if (control[0]) {
    31. updateTimer->stop();
    32. lock->lock();
    33. condition->wait(lock);
    34. lock->unlock();
    35. updateTimer->start(timerVal); }
    36. }
    37. void ThreadWatcher::finishedHandler(int t) {
    38. emit refresh(t);
    39. alive[t] = false;
    40. threadsLeft--;
    41.  
    42. seconds[t] = (float)runTime[t] / 1000.0f;
    43. if (seconds[t] == 0.0f)
    44. seconds[t] = 0.001f;
    45. emit time(t, "Time: "+QString::number(seconds[t],'g',3));
    46.  
    47. if (isFirst) {
    48. isFirst = false;
    49. min = t;
    50. emit ratio(t, "Ratio: 1x"); }
    51. else {
    52. float timeRatio = seconds[t]/seconds[min];
    53. if (timeRatio < 1.0f)
    54. fixRatios(t);
    55. else
    56. emit ratio(t, "Ratio: "+QString::number(timeRatio,'g',3)+"x"); }
    57. }
    58. inline void ThreadWatcher::fixRatios(int t) {
    59. long minTime = runTime[min];
    60. long x;
    61.  
    62. for (int i=0; i<6; i++) {
    63. if (!alive[i]) {
    64. x = runTime[i];
    65. if ((x > -1) && (x < minTime)) {
    66. minTime = x;
    67. min = i; } } }
    68.  
    69. for (int j=0; j<6; j++) {
    70. if (!alive[j]) {
    71. if (j==min)
    72. emit ratio(j, "Ratio: 1x");
    73. else
    74. emit ratio(j, "Ratio: "+QString::number(seconds[j]/seconds[min],'g',3)+"x"); } }
    75. }
    To copy to clipboard, switch view to plain text mode 

Similar Threads

  1. QThread and QTimer
    By sivrisinek in forum Qt Programming
    Replies: 4
    Last Post: 30th April 2009, 16:41
  2. QThread & QTimer
    By hosseinyounesi in forum Qt Programming
    Replies: 5
    Last Post: 13th April 2009, 08:22
  3. QTimer and QThread in Qtopia 4.2.0
    By mellibra in forum Qt for Embedded and Mobile
    Replies: 3
    Last Post: 25th October 2007, 08:26
  4. QTimer and QThread
    By TheKedge in forum Qt Programming
    Replies: 4
    Last Post: 21st September 2006, 14:52
  5. Qthread n QTimer Problem
    By quickNitin in forum Qt Programming
    Replies: 5
    Last Post: 8th June 2006, 14:12

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.