Search code examples
multithreadingqtc++11qthread

Cannot create children for a parent that is in a different thread qt c++


I am trying to run a separate thread from the main thread where mqttclient is initialised and moved to a new thread.

Previously there were problems of signal slot. Which i resumed by creating a separate class (mqtthandler) running from main thread and connected to mqttclient class via signal slots which are delivered to qml.

I tested the codes by running them in same thread. They all work.

Now when I tried to move mqttclient in another thread it causes problem. I know the reason why it is giving me the problem but I dont know, the solution to it.

The problem creator is mqttclient constructor because it connecttohost in constructor only. I need to connecttohost mqttclient but somehow in new thread.

I also tried to make connecttohost in subscribe function (of mqttclient itself) which at later stage is called via mqtthandler which is called by qml. Still it doesnt work.

main.cpp

  QGuiApplication app(argc, argv);

    QThread mqttclientThread;

    // When the application is quitting, so should the worker thread
    QObject::connect(&app, &QCoreApplication::aboutToQuit, &mqttclientThread, &QThread::quit);

    MqttClient * mqttclientObj = new MqttClient;

    mqttclientObj->moveToThread(&mqttclientThread);

    mqttclientThread.start();

    qDebug() << "Main client status " << mqttclientObj->state();

    MqttHandler * mqttHandlerObj = new MqttHandler;
    QObject::connect(mqttHandlerObj, &MqttHandler::mqtthandler_getaddr_signal, mqttclientObj, &MqttClient::getAddr);
    QObject::connect(mqttclientObj, &MqttClient::sensorValueChanged, mqttHandlerObj,&MqttHandler::mqtthandler_sensorValueChanged_slot);


    qDebug() << "Main " << QThread::currentThread();  

mqttclient.cpp

MqttClient::MqttClient(QObject *parent)
    : QMqttClient(parent)
{

    this->setHostname("127.0.0.1");
    this->setPort(1883);
    this->connectToHost();



    m_listTopic.clear();
    vSimulateSensorValues();
   connect(this, SIGNAL(publishsignals(QString, QString)),this, SLOT(publishmsg(QString,QString)));
}

MqttSubscription* MqttClient::subscribe(const QString &topic)
{

    auto sub = QMqttClient::subscribe(topic, 0);

    MqttSubscription*  result = new MqttSubscription(sub, this);

    qDebug() << "subscribe " << QThread::currentThread();

    m_listTopic.append(topic);

    return result;
}

MqttSubscription::MqttSubscription(QMqttSubscription *s, MqttClient *c)
    : sub(s),
      client(c)
{    
    connect(sub, &QMqttSubscription::messageReceived, client, &MqttClient::handleMessage);
    qDebug() << "mqttsubconn " << QThread::currentThread();
}

MqttSubscription::~MqttSubscription()
{
}

The Error

Main client status  1
Main  QThread(0x7fcfa7400530)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QMqttConnection(0x7fcfa75a3008), parent's thread is QThread(0x7fcfa7400530), current thread is QThread(0x7ffee1cb4a30)
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread

/*****************************************************************************************************************************************************************************************************************************/

Update

updated mqttclient class

    MqttClient::MqttClient(QObject *parent)
        : QMqttClient(parent)
    {
    }

    void MqttClient::Init()
    {
        this->setHostname("127.0.0.1");
        this->setPort(1883);
        this->connectToHost();

        QThread::sleep(10);

        m_listTopic.clear();
        vSimulateSensorValues();
        connect(this, &MqttClient::publishsignals, this, &MqttClient::publishmsg);
    }

    MqttSubscription* MqttClient::subscribe(const QString &topic)
    {

     //   this->setParent(this);

        auto sub = QMqttClient::subscribe(topic, 0);
        qDebug() << "state " << this->state();

        MqttSubscription*  result = new MqttSubscription(sub, this);

        qDebug() << "subscribe " << QThread::currentThread();

        m_listTopic.append(topic);

        return result;
    }

void MqttClient::handleMessage(const QMqttMessage &qmsg)
{
    emit MqttClient::sensorValueChanged(qmsg.payload(),qmsg.topic().name());
    qDebug() << "Handle " << QThread::currentThread();
}

void MqttClient::getAddr(QString value)
{

    qDebug() << "c++ " + value;
    subscribe(value);
    qDebug() << "getaddr " << QThread::currentThread();
}

    MqttSubscription::MqttSubscription(QMqttSubscription *s, MqttClient *c)
        : sub(s),
          client(c)
    {
        qDebug() << "Here " << client->state();
        qDebug() << client;

        connect(sub, &QMqttSubscription::messageReceived, client, &MqttClient::handleMessage);
        qDebug() << "mqttsubconn " << QThread::currentThread();
    }

    MqttSubscription::~MqttSubscription()
    {
    }

and main.cpp

QGuiApplication app(argc, argv);

QThread mqttclientThread;

// When the application is quitting, so should the worker thread
QObject::connect(&app, &QCoreApplication::aboutToQuit, &mqttclientThread, &QThread::quit);

MqttClient * mqttclientObj = new MqttClient;

mqttclientObj->moveToThread(&mqttclientThread);

QObject::connect(&mqttclientThread, &QThread::started, mqttclientObj, &MqttClient::Init);
mqttclientThread.start();

MqttHandler * mqttHandlerObj = new MqttHandler;
QObject::connect(mqttHandlerObj, &MqttHandler::mqtthandler_getaddr_signal, mqttclientObj, &MqttClient::getAddr);
QObject::connect(mqttclientObj, &MqttClient::sensorValueChanged, mqttHandlerObj,&MqttHandler::mqtthandler_sensorValueChanged_slot);


qDebug() << "Main " << QThread::currentThread();

The update gave rise to another problem

    Main  QThread(0x7f925750e790)
    state  1
    Here  1
    MqttClient(0x7f9257600f90)
    QObject::connect(QMqttSubscription, MqttClient): invalid null parameter
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread

Solution

  • If the problem is, as you suggest, the call to connectToHost in the MqttClient constructor then why not simply move the offending code into a slot?

    MqttClient::MqttClient(QObject *parent)
        : QMqttClient(parent)
    {
    }
    
    void MqttClient::init ()
    {
        this->setHostname("127.0.0.1");
        this->setPort(1883);
        this->connectToHost();
        m_listTopic.clear();
        vSimulateSensorValues();
        connect(this, &MqttClient::publishsignals, this, &MqttClient::publishmsg);
    }
    

    Then in main connect that slot to the QThread::started signal...

        .
        .
        .
    MqttClient *mqttclientObj = new MqttClient;
    mqttclientObj->moveToThread(&mqttclientThread);
    
    /*
     * Connect MqttClient::init slot to QThread::started signal.
     */
    connect(&mqttclientThread, &QThread::started, mqttclientObj, &MqttClient::init);
    mqttclientThread.start();
        .
        .
        .