Can you help me to find where my object is living?
-
Hi,
Yesterday I encountered an interesting scenario that I would like to discuss with you.
To describe it as easy as possible I made a small example of that scenario.
Let's first explain it:
I have three objects MainWindow, MyObject1 and MyObject2.From the MainWindow I create MyObject1.
MyObject1 is moved to its own thread, let's call it MyThread.
Signal/Slots connections are done to well clean MyThread when MyObject1 is deleted.
=> MyObject1 is living in MyThread.MyObject2 is a single tone object, and have a method startMyObject2Timer which start a timer with a period of 1 second.
=> MyObject2 is living in MyThread.After 5 seconds MyObject2 is deleted, done from QMainWindow.
This trigger MyThread to also be deleted.Results:
The timer in MyObject2 is not timing out anymore.Question:
Where is living MyObject2 since the thread where he was living has been deleted?Here is the code of that scenario:
MainWindow
@
#include <QDebug>
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);qDebug() << this->metaObject()->className() << __func__ << "THREAD :" << QThread::currentThreadId(); m_object1 = new MyObject1(); QThread *thread1 = new QThread(); m_object1->moveToThread(thread1); connect (thread1, SIGNAL(started()), m_object1, SLOT(process())); connect (m_object1, SIGNAL(destroyed()), thread1, SLOT(quit())); connect (thread1, SIGNAL(finished()), this, SLOT(onThreadFinished())); connect (thread1, SIGNAL(finished()), thread1, SLOT(deleteLater())); thread1->start(); connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); m_timer.start(5000);
}
MainWindow::~MainWindow()
{
delete ui;
}void MainWindow::onTimeout()
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();if (m_object1 != NULL) { delete m_object1; m_object1 = NULL; }
}
void MainWindow::onThreadFinished()
{
qDebug() << this->metaObject()->className() << func;
}
@MyObject1
@
#include <QDebug>#include "myobject1.h"
#include "myobject2.h"MyObject1::MyObject1(QObject *parent) :
QObject(parent)
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();
}MyObject1::~MyObject1()
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();
}void MyObject1::process()
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();MyObject2::instance()->startMyObject2Timer();
}
#ifndef MYTHREAD1_H
#define MYTHREAD1_H#include <QThread>
#include <QTimer>
#include <QTcpSocket>
#include "myobject2.h"class MyObject1 : public QObject
{
Q_OBJECT
public:
explicit MyObject1(QObject *parent = 0);
~MyObject1();signals:
public slots:
void process();protected:
private:
};void CbTest(void);
#endif // MYTHREAD1_H
@MyObject2
@
#include <QDebug>
#include <QThread>
#include "myobject2.h"MyObject2::MyObject2(QObject *parent) :
QObject(parent)
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();
}MyObject2::~MyObject2()
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();
}void MyObject2::startMyObject2Timer()
{
m_timer = new QTimer();
m_timer->setInterval(1000);
connect (m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
m_timer->start();
}void MyObject2::onTimeout()
{
qDebug() << this->metaObject()->className() << func << "THREAD :" << QThread::currentThreadId();
}#ifndef MYOBJECT2_H
#define MYOBJECT2_H#include <QTimer>
#include <QObject>class MyObject2 : public QObject
{
Q_OBJECT
public:
static MyObject2* instance()
{
static MyObject2* m_myObject2 = NULL;
if (m_myObject2 == NULL)
{
m_myObject2 = new MyObject2();
}
return m_myObject2;
}
~MyObject2();void startMyObject2Timer();
signals:
public slots:
void onTimeout(void);protected:
private:
explicit MyObject2(QObject *parent = 0);QTimer *m_timer;
};
#endif // MYOBJECT2_H
@
-
Hi tlvju,
Maybe I missed it, but I do not see where *m_object2 is instantiated. You have the declration:
@private:
MyObject2 *m_object2;@In MyObject1, but it is never set to anything... there is no m_object = new MyObject2(...);
Is this intentional?, maybe this is to do with the issue? - if you instantiate it within the process() function (which I assume is only called once after the thread is started) then it will be instantiated within the thread that Object1 is running in.
Do you have the output to your code showing all the thread IDs and the constructor and destructor function calls?
- EDIT -
Oh wait... I see it now, you have this static function and object within MyObject2 and you call it with "instance" function to return the pointer to the object.
However, since you created/instantiated the MyObject2 instance within MyObject1, which in turn is within thread1, then the instance of MyObject2 will be within thread1.
So when you stop thread1 the instance of MyObject2 will no longer be running (even if it is not deleted properly).
An object is instantiated within the thread that calls the code to instantiate it, in this case thread1.
-
Hi code_fodder,
MyObject2 is a single tone object, and it is created from MyObject1 process function by calling the static function MyObject2::instance().
Here is the output of the code:
@
MainWindow MainWindow THREAD : 0x3a8
MyObject1 MyObject1 THREAD : 0x3a8
MyObject1 process THREAD : 0x1218
MyObject2 MyObject2 THREAD : 0x1218
MyObject2 onTimeout THREAD : 0x1218
MyObject2 onTimeout THREAD : 0x1218
MyObject2 onTimeout THREAD : 0x1218
MyObject2 onTimeout THREAD : 0x1218
MainWindow onTimeout THREAD : 0x3a8
MyObject1 ~MyObject1 THREAD : 0x3a8
MainWindow onThreadFinished
@ -
yes... this looks exactly as it should. MyObject2 is within thread1 (see my - EDIT -), I missed the instance() call and updated my post for it...
Still, you should remove the *m_object2 member if its not used : )
-
It's tricky scenario, and I wanted to know the proper way of handling it.
Indeed MyObject2 is a single tone object, in that case the first object which will call the instance method of MyObject2 will create it. Then MyObject2 will live in the context of his creator.
What can be done is to move MyObject2 in its own thread, or call the instance method from a place where the thread attached to it will not be deleted.
-
It needs to be instantiated in a thread that will not be deleted.... as you say, maybe its own thread.
Somthing like (written by hand, so forgive any errors):
@
SingletonObj *sObj = new SingletonObj();
QThread *signletonThread = new QThread();
sObj->moveToThread(signletonThread);
QObject::connect(signletonThread, SIGNAL(started()), SingletonObj, SLOT(run()));
signletonThread->start();
// Then implement run() within SingletonObj to do its startup stuff...
@Do that in MainWindow() constructor. Remember, do NOT call functions directly from you class otherwise anything that is instantiated within the class will live in the calling thread :o
Then the best way to access the singleton object would be via signals/slots. Its not really safe to have a pointer to an object that lives in a different thread. There must be some very specific reason to do that. If you have one please explain it :)
Otherwise, this "singleton" can just sit there in its own thread quite happily, and answer slot/signal calls to it.