2
votes

I want to inform an object when a thread has finished running. However, I cannot get the thread to exit properly. I have the following code:

Processor.cpp

thread = new QThread;
tw = new ThreadWorker;
connect(tw, SIGNAL(updateStatus(QString)), this, SLOT(statusUpdate(QString)));
tw->doSetup(thread, strDic);
tw->moveToThread(thread);
thread->start();

while(thread->isRunning())
{
}

qDebug() << "Thread Finished";

ThreadWorker.cpp

void ThreadWorker::doSetup(QThread *thread, const string &path)
{
  _strPath = path;
  connect(thread, SIGNAL(started()), this, SLOT(run()));
  connect(this, SIGNAL(finished()), thread, SLOT(quit())); //tried terminate() also
}


void ThreadWorker::run()
{
  DirectorySearch dicSearch;
  vector<string> vecFileList = dicSearch.getFileList(_strPath);
  emit updateStatus("Directory Fetched");
  emit finished();
}

The quit() slot does not seem to stop the thread (QThread::isFinished never returns true). Can someone guide me in the right direction?

(Note: ThreadWorker does not inherit from QThread)

4
Just for test... try to add QCoreApplication::instance()->processEvents() in your while loop. This is only a suggestion, so i'm not posting it as answer. this may work because QThread itself is in main thread, so its OWN slots may be called in main event loop, instead of slots of ThreadWorker, which should execute in QThread.Raiv

4 Answers

4
votes

Assuming that Processor.cpp is running in your main thread, the while(thread->isRunning()) loop has your main thread completely tied up. This means that your application's event loop cannot do any processing so the signalupdateStatus() for example, will never get processed. As mentioned in the comments, since the QThread object is created by the main thread, its signals won't work either since they will also require the main event loop to be doing its thing. Besides, if you are waiting in your main thread for your worker thread to do something, why use a worker thread at all? :)

Try removing the while loop, add a slot workDone() (or whatever you want to call it) to Processor.cpp and connect that to your Threadworker's finished() signal.

3
votes

I had the same problem and found the answer. Here is my question: What is the use of QThread.wait() function?

To solve your problem, you don't need to run the QCoreApplication::instance()->processEvents() in your while loop, what you need to do is, instead of invoking the quit() which tries to send a signal to your creating thread's event loop (which is now blocked by the while loop), you have to call it directly.

So for your code, drop the line:

connect(this, SIGNAL(finished()), thread, SLOT(quit())); //tried terminate() also

And instead of:

emit finished();

Use:

this->thread()->quit();

Tada... problem solved. Lesson learned: don't try to exit a worker thread by the qt signal-slot mechanism from within it, because your signals do not end up where they are supposed to (your worker thread's event loop), but they end up in the creating thread instead. You never know what that thread is doing, and if its event loop is running or not, and this shouldn't be of business to your worker thread anyways... Instead, call the quit directly.

1
votes

You can use Qt::DirectConnection:

connect(this, SIGNAL(finished()), thread, SLOT(quit()), Qt::DirectConnection); 

This stops the thread.

-1
votes

Instead of doing your 'doSetup' function... before you moveToThread, setup connections between SINGALS on tw's parent and SLOTS in tw.

I would do 4 connections. First is to the run method in ThreadWorker. Thats simple and self explainatory enough.

Second is from your finished signal to the third SIGNAL connection below. A SIGNAL that quits the thread

Third to a SIGNAL that should call the terminate() slot of the thread. This will effectively close the event loop setup when you connect to the run method (exec is auto called when you do a start()) and since your run method isn't a loop of some sort, will close the thread without issue.

Forth is from the thread's terminated() SIGNAL to a SLOT in tw's parent. This will show you when the thread is dead if you want to do something at that point.

You do the above connections (if you need to pass in the string, add a variable to the run method and corresponding SIGNAL connection and you'll have the data), move to thread, thread start, then do the SIGNAL attached to the run method. Let it do its thing. When its finished, it will do a finished signal that gets tied to another signal that gets tied to the threads terminated slot. This will kill the event loop and exit the thread, pushing a terminated signal out so you can then do something.