0

MyClass object has a worker QObject, that is working in another thread:

class MyClass {
....
private:
   QThread thread;
   Worker worker; // inherits QObject
};

...

worker.moveToThread(&thread);

Now when I call thread.exit() my worker thread stops instantly. I would prefer it to finish all pending events, then quit.

I have tried something like "queued exit":

connect(this, SIGNAL(signalFinish()),
        &thread, SLOT(quit()),Qt::QueuedConnection);

 ...

void MyClass::finish()
{
    emit signalFinish();
    worker.disconnect(); // do not queue any more events
    thread.wait();
}

but it doesn't work. It will wait forever...

How to I stop QThread after it's event loop will process all pending events?

Garf365
  • 3,619
  • 5
  • 29
  • 41
user2449761
  • 1,169
  • 13
  • 25
  • maybe have a `QuitThread` slot in your worker (which will quit the current thread `QThread::currentThread()->quit();`), and connect to it instead of the `thread`'s `quit` slot. that way the `QuitThread` slot will get executed after all previously queued slots are executed. . . – Mike Jul 06 '16 at 12:51

2 Answers2

1

The thread and MyClass instances both live in the same thread - likely the main thread:

MyClass::MyClass() {
  ...
  Q_ASSERT(this->thread() == thread.thread());

Yet the events you're queuing to the worker object go to the worker thread:

  worker.moveToThread(&thread);
  Q_ASSERT(worker.thread() == &thread);
  Q_ASSERT(worker.thread() != thread.thread());

The queued connection is precisely the wrong thing to do because as soon as you wait on the worker thread, the main thread's event loop won't run and won't issue any more events to the thread. The quit call will never be delivered, since it's delivered in the main thread, but the main thread is blocked.

Instead, take the advantage of quit being a thread-safe method. Call it from the worker thread itself:

//                                    vvvvvvv thread context object for the call
connect(this, &MyClass::signalFinish, &worker, [this]{ thread.quit(); });
// the `quit()` will execute in `worker.thread()`

Note that the thread context object is an object whose thread() the call will be executed in. It should not be a QThread instance, in most cases!

When the signalFinish is emitted, a QMetaCallEvent carrying the functor above will be queued in the worker thread's queue, and will get executed after any existing call (and other) events are processed first.

Since you're calling finish in the main thread, you probably don't want to wait on the thread since that will block the GUI and make your application unresponsive.

As it seems you're submitting job items to a worker thread, you might be better served by QtConcurrent::run that submits job items to a thread pool, and does all the thread management for you. See this answer for a complete example. See this answer and that answer for some related tidbits.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

See this relevant StackOverflow answer.

You can use QThread::exec() call to run your thread in the event loop. The thread will run it until you tell your thread to exit by calling QThread::exit(). So some sample code can look like this:

void DownloadWorker::run()
 {
    DownloadManager* pDownloadManager = new DownloadManager(this);
    connect(pDownloadManager, SIGNAL(finished()), SLOT(exit()));
    connect(pDownloadManager, SIGNAL(error()), SLOT(exit()));
    pDownloadManager->download();
    exec();
 }

That would guarantee you that your thread won't quit until the "finished()" signal of your DownloadManager is issued.

Note: Here I put an example of how to solve your problem but I don't know your whole app code. That means there is not guarantee this code is thread safe and consistent. You need to take care of the mutexes and all the correct synchronization yourself. Be very careful ! Working with such a "low level" thread API requites good understanding of multithereading.

Community
  • 1
  • 1
Daniel Arnett
  • 493
  • 2
  • 8
  • 18