Results 1 to 18 of 18

Thread: Newbie threading question

  1. #1
    Join Date
    Apr 2007
    Location
    seattle
    Posts
    2
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Newbie threading question

    Hi,

    I'm new to Qt and threading, so I'm probably missing something obvious. I'm trying to use a thread in my application (subclassing QThread), and everything seems to be working. However, when the thread goes into a time consuming operation, my whole GUI freezes up.

    I have created a simplified version which is available at http://dsarkar.fhcrc.org/qtthread/. Basically, the main thread and the secondary thread both start a timer (1 and 2 seconds each) that print a message to the console when they timeout(). When I start the application, both sets of messages are printed as expected. There is a menu action that emits a signal that is connected to a slot in the thread making it sleep for 10 seconds. When this is triggered, I would have expected the main thread to keep on going. Instead, both sets of timers stop and the whole GUI freezes up for 10 seconds.

    The full code is available on the website. Here are the main parts:

    myMainWindow.cpp:

    Qt Code:
    1. MyMainWindow::MyMainWindow()
    2. {
    3. createActions();
    4. createMenus();
    5. mythread = new MyThread();
    6. QTimer *timer = new QTimer(this);
    7. connect(timer, SIGNAL(timeout()),
    8. this, SLOT(reportStatus()));
    9. timer->start(2000);
    10. connect(this, SIGNAL(needSleep(unsigned int)),
    11. mythread, SLOT(sleepFor(unsigned int)));
    12. }
    13.  
    14. void MyMainWindow::reportStatus()
    15. {
    16. printf("Main eventloop timeout event\n");
    17. }
    18.  
    19. void MyMainWindow::createActions()
    20. {
    21. sleepAct = new QAction("&Make Thread Sleep", this);
    22. sleepAct->setShortcut(tr("Ctrl+S"));
    23. connect(sleepAct, SIGNAL(triggered()),
    24. this, SLOT(makeThreadSleep()));
    25. }
    26.  
    27.  
    28. void MyMainWindow::makeThreadSleep()
    29. {
    30. emit needSleep(10);
    31. }
    To copy to clipboard, switch view to plain text mode 

    myThread.cpp:
    Qt Code:
    1. #include <unistd.h>
    2.  
    3. MyThread::MyThread(QObject * parent)
    4. : QThread(parent)
    5. {
    6. QTimer *ttimer = new QTimer(this);
    7. connect(ttimer, SIGNAL(timeout()),
    8. this, SLOT(reportThreadStatus()));
    9. ttimer->start(1000);
    10. return;
    11. }
    12.  
    13. void MyThread::reportThreadStatus()
    14. {
    15. printf("Thread eventloop timeout event\n");
    16. }
    17.  
    18. void MyThread::sleepFor(unsigned int t)
    19. {
    20. printf("Thread going to sleep for %d seconds...\n", t);
    21. sleep(t);
    22. printf("Thread waking up...\n");
    23. }
    24.  
    25.  
    26. void MyThread::run()
    27. {
    28. exec();
    29. }
    To copy to clipboard, switch view to plain text mode 

    main.cpp:

    Qt Code:
    1. int main(int argc, char *argv[])
    2. {
    3. QApplication app(argc, argv);
    4. MyMainWindow mainWin;
    5. mainWin.show();
    6. mainWin.mythread->start();
    7. return app.exec();
    8. }
    To copy to clipboard, switch view to plain text mode 

    Does anyone see what I'm doing wrong? Thanks,
    -Deepayan

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

    Default Re: Newbie threading question

    Don't include unistd.h and use QThread::sleep() instead of sleep(). Maybe this will help.

  3. #3
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    This will not work! Because:

    1. Current situation
    Qt Code:
    1. connect(this, SIGNAL(needSleep(unsigned int)),
    2. mythread, SLOT(sleepFor(unsigned int)) , Qt::QueuedConnection);
    To copy to clipboard, switch view to plain text mode 
    This will post an event to the thread's event queue instead of calling sleepFor directly. Wrong again because sleepFor will be called from the thread's event queue and also affects the applications event dispatcher ( will freeze again ).
    I have attached a screenshot for the second case. As you can see the event dispatcher does not exit until sleepFor finishes.

    Solution:
    Do not sleep in the event handler.

    EDIT: Actually, the first case does not apply ( ). It is still QueuedConnections, since is another thread.

    Regards
    Attached Images Attached Images
    Last edited by marcel; 15th April 2007 at 10:13.

  4. #4
    Join Date
    Aug 2006
    Location
    Switzerland
    Posts
    52
    Thanked 13 Times in 11 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Newbie threading question

    Your thread object (created in MyMainWindow constructor) lives in main thread (another name is gui thread), so all its slots are executed in main thread and that's why your GUI freezes. When I am using threads I usually create two classes. One class is a class with all slots and another is a thread class for the previous one. It looks like below:
    Qt Code:
    1. // header file
    2. class MyObjectThread : public QThread {
    3. Q_OBJECT;
    4. protected:
    5. void run() {
    6. exec();
    7. }
    8. };
    9.  
    10. class MyObject : public QObject {
    11. Q_OBJECT;
    12. public:
    13. MyObject();
    14. public slots:
    15. void slDoSomething();
    16. private:
    17. MyObjectThread * thread;
    18. private slots:
    19. void slThreadStarted();
    20. };
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // implementation file
    2. MyObject::MyObject() {
    3. thread = new MyObjectThread();
    4. connect(thread, SIGNAL(started()), SLOT(slThreadStarted()));
    5. this->moveToThread(thread);
    6. thread->start();
    7. }
    8.  
    9. void
    10. MyObject::slThreadStarted() {
    11. // do something or nothing
    12. }
    13.  
    14. void
    15. MyObject::slDoSomething() {
    16. // do something
    17. }
    To copy to clipboard, switch view to plain text mode 
    Now I am sure that MyObject lives in its own thread and its slots will be executed in own thread.
    The Wheel weaves as the Wheel wills.

  5. #5
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    Sorry, not true. I actually tested it... Read my previous post. The same thing happens...
    Anyway the thread is only started in main(), but is created in MyMainWindow.

    Regards

  6. #6
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    To Danadam:
    this->moveToThread(thread);
    ---Won't ever work considering the thread was created in the main window (a widget ), and all widgets must live in the gui thread. You can't just move them to another thread.

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

    Default Re: Newbie threading question

    Quote Originally Posted by marcel View Post
    Sorry, not true. I actually tested it... Read my previous post. The same thing happens...
    Anyway the thread is only started in main(), but is created in MyMainWindow.
    There is something wrong with your code. It uses sendEvent() to deliver the event to the queue and that causes the calling thread to be blocked. With queued connections the event should be delivered using postEvent(). Are you by any chance using Qt 4.3 beta? The effect you experience seems to fit to the blocked queued connection type.

  8. #8
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    No, it's 4.2.2.

    EDIT - with the code from the first post...
    Wasn't the event posted when the signal was emitted? What you see in the call stack is the posted event being processed.
    Last edited by marcel; 15th April 2007 at 10:43.

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

    Default Re: Newbie threading question

    Oh, I see what's wrong You're calling a slot from the thread and not an object created in the thread. The QThread instance lives in the thread that created the object (the main thread in this case). So when you call a slot from the QThread object, it is executed in the context of the main thread and not of the worker thread. Either move the slot to an object created in the run() method of your thread or change the thread affinity of the QThread object using moveToThread(). Just make sure to do it after the thread is actually started.

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

    Default Re: Newbie threading question

    Here is a small example to demonstrate what I mean.
    Attached Files Attached Files

  11. #11
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    I think you complicated things a bit there... It works by calling myThread->moveToThread(myThread) right after starting the thread, in the main window constructor.
    A good idea nevertheless....

    Regards
    Last edited by marcel; 15th April 2007 at 11:21.

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

    Default Re: Newbie threading question

    I didn't complicate anything. It was just an example to show that the affinity changes and the connection type changes from Direct to Queued.

  13. #13
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    It was all about moving the event processing for the thread to the thread event handler...
    It can be seen now on the call stack that the event is treated in MyThread::exec() not QApplication::exec(), as before.

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

    Default Re: Newbie threading question

    That's exactly what a "queued" connection means.

  15. #15
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    Yes, I know. It was queued before, but it was treated in the GUI event handler, not in the thread's event handler. That is why it blocked the interface.

    Regards

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

    Default Re: Newbie threading question

    No, it wasn't queued. If you don't pass a connection type to connect() (like it is the case here) it defaults to Qt::AutoConnection, which means that when a signal is emitted and Qt iterates slots that are connected, it checks if the caller and callee live in the same thread. If so, it calls the slot directly (like in this situation). Otherwise it posts an event to the receiving thread.

    BTW. I'm not sure it is reliable to change the thread affinity immediately after calling QThread::start(). The thread may not be started at that point yet. It's safer to move the object on the QThread::started() signal.

  17. #17
    Join Date
    Feb 2006
    Location
    Romania
    Posts
    2,744
    Thanks
    8
    Thanked 541 Times in 521 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Newbie threading question

    True.
    Also, in the Assistant, there is stated:
    With auto connections (the default), the behavior is the same as with direct connections if the signal is emitted in the thread where the receiver lives; otherwise, the behavior is that of a queued connection.
    I'm not sure it is reliable to change the thread affinity immediately after calling QThread::start(). The thread may not be started at that point yet. It's safer to move the object on the QThread::started() signal.
    True.

  18. #18
    Join Date
    Apr 2007
    Location
    seattle
    Posts
    2
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Newbie threading question

    Quote Originally Posted by marcel View Post
    Sorry, not true. I actually tested it... Read my previous post. The same thing happens...
    Anyway the thread is only started in main(), but is created in MyMainWindow.

    Regards
    I think you identified the problem correctly. After reading the docs a bit more, I realised that only objects that are created in myThread::run() actually live in the new thread. Moving things around accordingly solves my problem.

    Thanks for all the quick replies.

    Deepayan

Similar Threads

  1. Replies: 1
    Last Post: 15th March 2007, 21:45
  2. QThread exit()/quit() question
    By TheKedge in forum Qt Programming
    Replies: 1
    Last Post: 28th August 2006, 15:38
  3. Tipical newbie question
    By Dark_Tower in forum Newbie
    Replies: 2
    Last Post: 24th March 2006, 07:24
  4. newbie question about qtextedit fields
    By otortos in forum Qt Programming
    Replies: 1
    Last Post: 23rd February 2006, 19:21

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.