How can I get QThread to release a QObject allocated to the heap without leakage?

My situation is that I have a QWidget-based class, MyWidget, which will create a class created by QThread (WorkerThread) to do some trouble-free blocking work in the run () method. The result is an instance of the allocated heap of the QObject (DataClass) class, which is then received and processed by MyWidget. However, MyWidget is temporary widgets and can be removed while WorkerThread is still running due to user action.

Here is some pseudo code to illustrate this:

#include <QThread>
#include <QWidget>

class DataClass : public QObject {
    Q_OBJECT
public:
    // contains some complex data
};

class WorkerThread : public QThread {
    Q_OBJECT
public:
    virtual void run() {
        DataClass *result = new DataClass;
        doSomeReallyLongUninterruptibleWork(result);
        emit workComplete(result);
    }
signals:
    void workComplete(DataClass *);
};

class MyWidget : public QWidget {
    Q_OBJECT
public:
    void doBlockingWork() {
        WorkerThread *worker = new WorkerThread;
        connect(worker, &WorkerThread::finished, worker, &WorkerThread::deleteLater);
        connect(worker, &WorkerThread::workComplete, this, &MyWidget::processData);
        worker->start();
    }

public slots:
    void processData(DataClass *result) {
        // Do some stuff
        delete result;
        // Assuming MyWidget still exists when WorkerThread has finished, no memory has leaked
    }
};

"Qt" , , , . , , , , , .

, , WorkerThread , DataClass MyWidget, DataClass .

, , , MyWidget WorkerThread. , , DataClass ?

, , , , Qt , WorkerThread MyWidget, WorkerThread , . , , .

+4
2

(, QSharedPointer) :

DataClass *result = new DataClass;

QSharedPointer<DataClass> result = QSharedPointer<DataClass>(new DataClass);

- . , , .

+6

, , ( QObject). , . :

void run() override {
   auto result = new DataClass;
   doSomeReallyLongUninterruptibleWork(result);
   result->moveToThread(qApp->thread());   // added
   emit workComplete(result);
   QObject::connect(this, &QThread::finished, result, &QObject::deleteLater); // added
}

, deleteLater workComplete .

. , . :

...
QObject::connect(this, &QThread::finished, result, [result]{
  if (!result->parent()) result->deleteLater();
});

, , QSharedPointer workComplete, : a QSharedPointer : , .

DataClass , DataClass::thead() deleteLater :

  • : emit workComplete(result)
  • : result, result.thread() - .
  • :
  • : result.thread() nullptr, .

, . QObject DataClass, : . deleteLater , ..

, QObject , , . , . undefined:

  • : emit workComplete(result)
  • : result, result.thread() - .
  • : delete result. QObject::~QObject qApp->thread(), result->thread() - , .

, :

DataClass::~DataClass() {
  Q_ASSERT(thread() == nullptr || thread() == QThread::currentThread());
  ...
}

, , : deleteLater , , ..

deleteLater , , workComplete.

"" , , . , , QObject , . , , , . , QObject : thread() - , .

workComplete . - , - . , workComplete , , .

QTimer::singleShot(1000, w.data(), [&]{ w.reset(); }) 2500 , , .

:

// https://github.com/KubaO/stackoverflown/tree/master/questions/worker-shared-37956073
#include <QtCore>

struct DataClass : public QObject {
   DataClass() { qDebug() << __FUNCTION__; }
   ~DataClass() { qDebug() << __FUNCTION__; }
};
void doSomeReallyLongUninterruptibleWork(DataClass*) { QThread::sleep(2); }

class WorkerThread : public QThread {
   Q_OBJECT
public:
   void run() override {
      auto result = new DataClass;
      doSomeReallyLongUninterruptibleWork(result);
      result->moveToThread(qApp->thread());
      emit workComplete(result);
      QObject::connect(this, &QThread::finished, result, [result]{
         if (!result->parent()) {
            qDebug() << "DataClass is unclaimed and will deleteLater";
            result->deleteLater();
         }
      });
   }
   Q_SIGNAL void workComplete(DataClass*);
};

class MyWidget : public QObject {
   void processData(DataClass * result) {
      // Do stuff with result
      // Retain ownership (optional)
      if (true) result->setParent(this);
   }
public:
   void doBlockingWork() {
      auto worker = new WorkerThread;
      connect(worker, &WorkerThread::workComplete, this, &MyWidget::processData);
      connect(worker, &WorkerThread::finished, worker, &WorkerThread::deleteLater);
      worker->start();
   }
   ~MyWidget() { qDebug() << __FUNCTION__; }
};

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   QScopedPointer<MyWidget> w{new MyWidget};
   w->doBlockingWork();
   QTimer::singleShot(1000, w.data(), [&]{ w.reset(); });
   QTimer::singleShot(3000, qApp, &QCoreApplication::quit);
   return app.exec();
}

#include "main.moc"

QtConcurrent::run. , , , .

#include <QtConcurrent>

struct DataClass : public QObject {
   Q_SIGNAL void ready();
   Q_OBJECT
};

// Let not pollute the default pool with long-running stuff
Q_GLOBAL_STATIC(QThreadPool, longPool)

class MyWidget : public QObject {
   void processData(DataClass * result) {
      // Do stuff with result
      // Retain ownership (optional)
      if (true) result->setParent(this);
   }
public:
   void doBlockingWork() {
      auto result = new DataClass;
      connect(result, &DataClass::ready, this, [=]{ MyWidget::processData(result); });
      result->moveToThread(nullptr);
      QtConcurrent::run(longPool, [result]{
         result->moveToThread(QThread::currentThread());
         doSomeReallyLongUninterruptibleWork(result);
         result->moveToThread(qApp->thread());
         emit result->ready();
         QTimer::singleShot(0, result, [result]{
            if (!result->parent()) result->deleteLater();
         });
      });
   }
};
+1

All Articles