Qt报错:QObject无法为不同线程的父对象创建子对象
Qt跨线程创建QObject子对象报错解决
报错信息
QObject: Cannot create children for a parent that is in a different thread. (Parent is QUdpSocket(0x557d126bb830), parent's thread is QThread(0x557d11d1d680), current thread is QThread(0x557d126c64d0)
开发环境
- 编译器:GCC 13.2.1
- Qt版本:5.15.11
- Qt Creator版本:11.0.3
最小复现代码
main.cpp
int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow w; w.show(); UDPClient c(&w, QHostAddress("192.168.1.255"), 5001); MyProcessing p(NULL, "Hi!"); QTimer timer; timer.setInterval(1000); timer.setTimerType(Qt::TimerType::PreciseTimer); QObject::connect(&timer, SIGNAL(timeout()), &p, SLOT(onTimeout())); QObject::connect(&p, SIGNAL(sigNewDataReady(QByteArray)), &c, SLOT(onNewUDPDataReceived(QByteArray)), Qt::DirectConnection); p.start(); timer.start(); return a.exec(); }
udpclient.h
#ifndef UDPCLIENT_H #define UDPCLIENT_H #include <QObject> #include <QThread> #include <QUdpSocket> class UDPClient : public QObject { Q_OBJECT public: explicit UDPClient(QObject *parent = nullptr, QHostAddress aAddr = QHostAddress("192.168.0.20"), quint16 aPortNum = 0); public slots: void onNewUDPDataReceived(QByteArray data); private: QUdpSocket *udpSocket; quint16 portNum; QHostAddress addr; }; #endif // UDPCLIENT_H
udpclient.cpp
#include "udpclient.h" UDPClient::UDPClient(QObject *parent, QHostAddress aAddr, quint16 aPortNum) : QObject{parent} { portNum = aPortNum; addr = aAddr; udpSocket = new QUdpSocket(); } void UDPClient::onNewUDPDataReceived(QByteArray data) { udpSocket->writeDatagram(data, addr, portNum); }
myprocessing.h
#ifndef MYPROCESSING_H #define MYPROCESSING_H #include <QObject> #include <QThread> class MyProcessing : public QObject { Q_OBJECT public: MyProcessing(QObject *parent = nullptr, QByteArray text = "text"); void start() { mThread->start(); } signals: void sigNewDataReady(QByteArray); public slots: void onTimeout() {emit sigNewDataReady(QByteArray(mText)); } private: QByteArray mText; QThread *mThread; }; #endif // MYPROCESSING_H
myprocessing.cpp
#include "myprocessing.h" MyProcessing::MyProcessing(QObject *parent, QByteArray text) : QObject{parent} { mText = text; mThread = new QThread; this->moveToThread(mThread); }
问题原因
报错核心是跨线程调用了属于主线程的QUdpSocket方法:
MyProcessing对象被移到自身子线程运行- 信号
sigNewDataReady与槽onNewUDPDataReceived使用Qt::DirectConnection,导致槽函数直接在信号发射的子线程中执行 UDPClient和内部的udpSocket都属于主线程,子线程中调用udpSocket->writeDatagram时,QUdpSocket内部会创建临时子对象,其父对象是主线程的udpSocket,触发跨线程创建子对象的报错。
解决方案
方案1:修改信号槽连接类型
将Qt::DirectConnection改为默认的Qt::AutoConnection(可省略),Qt会自动判断线程,跨线程时自动用队列连接,让槽函数在UDPClient所属主线程执行:
QObject::connect(&p, SIGNAL(sigNewDataReady(QByteArray)), &c, SLOT(onNewUDPDataReceived(QByteArray))); // 或显式指定: // QObject::connect(&p, SIGNAL(sigNewDataReady(QByteArray)), // &c, SLOT(onNewUDPDataReceived(QByteArray)), // Qt::AutoConnection);
方案2:确保QUdpSocket操作在所属线程执行
若必须使用直接连接,可在UDPClient中将writeDatagram操作通过队列投递到自身线程:
修改udpclient.cpp的onNewUDPDataReceived方法:
void UDPClient::onNewUDPDataReceived(QByteArray data) { if (QThread::currentThread() != this->thread()) { QMetaObject::invokeMethod(this, "onNewUDPDataReceived", Qt::QueuedConnection, Q_ARG(QByteArray, data)); return; } udpSocket->writeDatagram(data, addr, portNum); }
方案3:将UDPClient移到子线程(可选)
若业务需要,可将UDPClient整体移到子线程,注意所有操作需通过信号槽或队列调用保证线程安全:
// 在main.cpp中添加 QThread* clientThread = new QThread; c.moveToThread(clientThread); clientThread->start();
内容的提问来源于stack exchange,提问作者Veljko
相关产品推荐
相关产品推荐

