6

I found this QThread example from here. It recommends to use QObject and QThread over than subclassing QThread.

class Worker : public QObject
{
   Q_OBJECT

public slots:
    void doWork() {
        /* ... */
    }
};

QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
thread->start();
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

My first question is when and how to delete the thread?

I have tried to connect finished to 2 slots, myTest and deleteLater. And I set a breakpoint in myTest, this never got triggered. So I suspect there is no finished signal, which mean the thread would not be deleted.

connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), this, SLOT(myTest()));

For the worker object, I emit a finished signal as the last instruction in doWork, and connect it to a slot in which I can delete the worker object. Is this the right way?

Qt version: 4.6.2

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jerry
  • 1,704
  • 5
  • 20
  • 40
  • 2
    possible duplicate of [How to release memory of Qthread object?](http://stackoverflow.com/questions/26714492/how-to-release-memory-of-qthread-object) – Iuliu Nov 28 '14 at 14:11
  • Just connect the thread `finished()` signal first to the worker and then to the thread's own `deleteLater()` slot – dtech Nov 28 '14 at 14:13
  • 3
    Something has to tell the thread to [quit](http://qt-project.org/doc/qt-5/qthread.html#quit), otherwise it will never emit the [`finished`](http://qt-project.org/doc/qt-5/qthread.html#finished) signal. – thuga Nov 28 '14 at 14:15
  • Ops, seems that my last comment only works for versions of Qt after 4.8, it will likely not work in 4.6.2. – dtech Nov 28 '14 at 14:16
  • 1
    emit some signal in your `Worker` at the end of `doWork` function and connect it to quit() slot of thread just before connection finished to deleteLater() etc – Shf Nov 28 '14 at 14:34
  • Thank you guys. Suppose I can make the thread life span the same as mainwindow, but for learning purpose, if I want to finish the thread earlier, should I manually call thread->exit() and assign it back to null? Or like @Shf said, connect signal to quit slot before connect finished to deleteLater? – Jerry Nov 28 '14 at 15:11

2 Answers2

10

Your thread exits normally when its worker method execution completed, but if you want to do some things in thread exit time, use the finished() signal and it is possible to deallocate objects that live in thread that has just ended, by connecting the finished() signal to QObject::deleteLater():

connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
Reza Ebrahimi
  • 3,623
  • 2
  • 27
  • 41
0

Your method of deleting later thread using connect() statement should work.

Your suggested method of deleting later worker after it is finished() should also work.

However, in order to trigger your slot myTest(), you will need to add more code:

  1. When worker is finished(), use connect() statements to call thread->quit(), worker->deleteLater(), and this->deleteLater().
  2. Use Qt::DirectConnection in the above connect() statements.

Following is a test example to implement the above solution, assuming that your class is named Tester:

File worker.h:

#ifndef WORKER_H
#define WORKER_H

#include <QObject>

class Worker : public QObject
{
Q_OBJECT
public:
    Worker();
    ~Worker();
signals:
    void finished();
public slots:
    void doWork();
};

#endif // WORKER_H

File worker.cpp:

#include "worker.h"

#include <QDebug>

Worker::Worker()
{
    qDebug() << "D/Worker==Worker";
}

Worker::~Worker()
{
    qDebug() << "D/Worker==~Worker";
}

void Worker::doWork()
{
    qDebug() << "D/Worker==doWork";

    // Do work here

    emit finished();
}

File tester.h:

#ifndef TESTER_H
#define TESTER_H

#include <QObject>

class Tester : public QObject
{
    Q_OBJECT
public:
    Tester();
    ~Tester();
public:
    void startTesting();
public slots:
    void myTest();
};

#endif // TESTER_H

File tester.cpp:

#include "tester.h"

#include "worker.h"

#include <QThread>
#include <QDebug>

Tester::Tester()
{
    qDebug() << "D/Tester==Tester";
}

Tester::~Tester()
{
    qDebug() << "D/Tester==~Tester";
}

void Tester::startTesting()
{
    qDebug() << "D/Tester==startTesting";

    QThread * thread = new QThread;
    Worker * worker = new Worker;
    worker->moveToThread(thread);
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::DirectConnection);
    connect(thread, SIGNAL(finished()), this,   SLOT(myTest()),      Qt::DirectConnection);
    connect(worker, SIGNAL(finished()), thread, SLOT(quit()),        Qt::DirectConnection);
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()), Qt::DirectConnection);
    connect(worker, SIGNAL(finished()), this,   SLOT(deleteLater()), Qt::DirectConnection);
    thread->start();
    QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

    // "thread" is deleted later.
    // "worker" is deleted later.
}

void Tester::myTest()
{
    qDebug() << "D/Tester==myTest";
}

File main.cpp:

#include <QCoreApplication>
#include <QObject>
#include <QDebug>

#include "worker.h"
#include "tester.h"

int main(int argc, char *argv[])
{
    qDebug() << "D/TestQThreadNewDelete==main";

    QCoreApplication a(argc, argv);

    Tester * tester = new Tester;

    tester->startTesting();

    // "tester" is deleted later in tester->onWorkerFinished().

    return a.exec();
}

The output of running this test application, using Qt 5.5.0 on Linux, is as follows:

D/TestQThreadNewDelete==main
D/Tester==Tester
D/Tester==startTesting
D/Worker==Worker
D/Worker==doWork
D/Tester==myTest
D/Worker==~Worker
D/Tester==~Tester

from which we can see that:

  1. Slot Tester::myTest() was triggered.
  2. Therefore, signal QThread::finished() should have been emitted earlier.
  3. Similarly, slot QThread::deleteLater() would be triggered also. This is likely to happen when the test application exits. However, debugging would be needed in order to confirm this, which I have not tried.
jonathanzh
  • 1,346
  • 15
  • 21
  • The last connect with Qt::DirectConnection is wrong and will make your app crash, as the Tester will be destroyed in the sub-thread. Don't mess with DirectConnection and threads. – galinette Sep 04 '15 at 16:06
  • You are right. My mistake of not going through each line to check. So don't use `Qt::DirectConnection` in the last `connect` statement. Thanks for pointing this out. – jonathanzh Sep 04 '15 at 16:20