PDA

View Full Version : Thread event loop not working when the main application is blocked.



kalos80
7th February 2012, 15:02
Hi,

I have a quite simple problem that is driving me crazy.

I have some operations that are performed on a thread. I have an object that's doing the heavy job, and I'm moving it to a thread, as suggested in http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
The object is created the following way



MainDialog::MainDialog(QWidget *parent, Qt::WindowFlags f)
: QDialog(parent, f)
{
/* ... */

myThread = new QThread(this);

myObject = new MyObject;
myObject->moveToThread(myThread);

connect( myThread, SIGNAL( started() ),
myObject, SLOT( start() ) );
connect( myObject, SIGNAL( finished() ),
myThread, SLOT( quit() ) );
}

Ideally when I start myThread the start() slot should be called on myObject and when myObject emits the finished() signal my thread should quit.

The implementation of MyObject is as simple as this


void MyObject::start()
{
QTimer::singleShot( 0, this, SLOT(doOperation()) );
}

void MyObject::doOperation()
{
if( stopped() )
{
emit finished();
return;
}

/* ... */

QTimer::singleShot(1000, this, SLOT(doOperation()));
}

void MyObject::stop()
{
QMutexLocker ml(&_mutex);

_stopped = true;
}

bool MyObject::stopped() const
{
QMutexLocker ml(&_mutex);

return _stopped;
}


Essentially an operation is performed periodically till the operation is not stopped through the stop() method.
On the main application I start the thread


void MainDialog::onStartClicked()
{
myThread->start();
}

and myObject starts working as expected.
The problem happens when I stop the object flow calling the stop function


void MainDialog::onStopClicked()
{
myObject->stop();

myThread->wait();

/* ... */
}


the wait() on myThread freezes my application and the quit() slot on myThread is never called.
I really do not understand why, since the thread has its own event loop.

I'm using Qt 4.7.3 on Windows.

Thanks in advance for your help.

wysota
7th February 2012, 15:23
This is because the thread object lives in the main thread and not the thread it represents. Thus if you emit a signal connected to the quit() slot, the call is queued so that it can be executed in the main thread. But since you call wait() in the main thread, the slot never has a chance to execute because the main thread is already locked by wait(). A solution is to either not call wait() at all (as it is not required) and instead simply connect the finished() signal to the deleteLater() slot or not call wait() in the onStopClicked() slot but instead connect a custom slot to the finished() signal and call wait() from that slot.

kalos80
7th February 2012, 15:44
wysota, thanks for your reply.


This is because the thread object lives in the main thread and not the thread it represents. Thus if you emit a signal connected to the quit() slot, the call is queued so that it can be executed in the main thread. But since you call wait() in the main thread, the slot never has a chance to execute because the main thread is already locked by wait().

I still do not understand why, since the QThread has its own event loop and since I move myObject to the thread, so I change its thread affinity, the events launched by myObject are handled by the event loop of the main thread and not by the event loop of the thread the object belongs to.


A solution is to either not call wait() at all (as it is not required) and instead simply connect the finished() signal to the deleteLater() slot

I need to wait the thread effectively finishes because I need to do some operations only after the thread has been stopped. I did not write them on the example code for simplicity reasons.


not call wait() in the onStopClicked() slot but instead connect a custom slot to the finished() signal and call wait() from that slot.

that could work, but consider the following case where I need to stop the thread when the application is closed



void MainDialog::closeEvent(QCloseEvent *ce)
{
myObject->stop();
}

void MainDialog::waitForOpFinished()
{
myObject->wait();

/* ..... */
}


I can not perform a wait on another slot, because since the application is being closed that slot could not be called

wysota
7th February 2012, 20:17
I still do not understand why, since the QThread has its own event loop
No, the thread has its own event loop, not the QThread object. You didn't move the QThread object to the new thread but some other object. Hence all events for the QThread instance are handled by the thread that created the object (aka the main thread).


I can not perform a wait on another slot
Sure you can.

kalos80
8th February 2012, 08:53
The thread has its own event loop, not the QThread object. You didn't move the QThread object to the new thread but some other object. Hence all events for the QThread instance are handled by the thread that created the object (aka the main thread).

Ahhhh, that would explain everything. So the event loop of myThread object is the main thread event loop, while the thread event loop processes events of thread's objects.
Thank you very much for making me see the light ;)



Sure you can.

My point is, since the finished() signal could be emitted when the the application is being closed, I'm not sure that all events in queue are processed before the application is closed, so the waitForOpFinished() slot could not be called.

I fixed the problem calling the thread quit() directly from the object



void MyObject::doOperation()
{
if( stopped() )
{
emit finished();

thread()->quit();

return;
}

wysota
8th February 2012, 09:56
My point is, since the finished() signal could be emitted when the the application is being closed, I'm not sure that all events in queue are processed before the application is closed, so the waitForOpFinished() slot could not be called.

When quit is requested you can first stop the thread and only when it's already done, quit() the main event loop.