Search code examples
c++qthreadworkerqobjectaffinity

Thread Affinity: Cannot create children for a parent that is in a different thread


I have seen a similar question, but I feel that I am implementing the correct pattern and still I can't get it done!

Well, I have a Gui to start and stop data acquisition from a serial port and display necessary communication messages. To keep the Gui responsive, I move the worker to a thread. I tried to implement thread affinity, according to: How to Use QThread in the Right Way and How To Really, Truly Use QThreads. When I click on start button, I receive;

QWinEventNotifier: event notifiers cannot be enabled from another thread
QWinEventNotifier: event notifiers cannot be enabled from another thread
QWinEventNotifier: event notifiers cannot be enabled from another thread
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x142cd390), parent's thread is QThread(0x1259b070), current thread is QThread(0x142db1f0)

What am I missing? Here is a part of the code related to my question:

Worker header

#ifndef COMPORT_H
#define COMPORT_H

#include <QObject>
#include <QDebug>
#include <QSerialPort>

class QTimer;

class ComPort : public QObject
{
    Q_OBJECT

public:
    explicit ComPort(const QString &portName, QObject* parent = 0);
    ~ComPort();

private:
    QSerialPort*    port;
    QString         portMsg;
    QByteArray      responseData;
signals:
    void finished();

private slots:
    void onReadyRead();
    void setupPort();

};

#endif // COMPORT_H

Worker cpp

#include "comport.h"

ComPort::ComPort(const QString &portName, QObject *parent)
    :QObject(parent)
{
    this->port = new QSerialPort(portName);
}

ComPort::~ComPort()
{
    port->close();
    delete port;
}

void ComPort::setupPort()
{
    port->setBaudRate(QSerialPort::Baud19200);
    port->setDataBits(QSerialPort::Data8);
    port->setFlowControl(QSerialPort::NoFlowControl);
    port->setParity(QSerialPort::NoParity);
    port->setStopBits(QSerialPort::OneStop);

    connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead()));

    *SOME CODE HERE*
}

void ComPort::onReadyRead()
{
    QByteArray bytes = port->readAll() ;
    qDebug() << "bytes:" << bytes <<"\n";
    responseData.append(bytes);
}

and Gui

#include "gui.h"
#include "ui_gui.h"

gui::gui(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::gui)
{
    ui->setupUi(this);
    connect(ui->startButton, SIGNAL(clicked()), this, SLOT(OnstartButtonClicked()));
}

gui::~gui()
{
    delete ui;
}

void gui::OnstartButtonClicked()
{
    QThread*  cThread = new QThread;
    ComPort*  cPort   = new ComPort(QString("COM4"));
    cPort->moveToThread(cThread);
    connect(cPort, SIGNAL(finished()), cThread, SLOT(quit()));
    connect(cPort, SIGNAL(finished()), cPort, SLOT(deleteLater()));
    connect(cThread, SIGNAL(finished()), cThread, SLOT(deleteLater()));
    connect(cThread, SIGNAL(started()), cPort, SLOT(setupPort()));
    cThread->start();
}

Solution

  • Thank to the answer from Kim Bowles Sørhus, I solved my problem. I was creating a serial port in the constructor of ComPort that is called in the main thread. When the cPort object is moved to the cThread the QSerialPort still has its thread affinity set to the original thread. A solution is to create the QSerialPort in ComPort::setupPort.