( QNativeSocketEngine)QObject:无法为位于不同线程中的父线程创建子级

( QNativeSocketEngine) QObject: Cannot create children for a parent that is in a different thread

本文关键字:线程 创建 于不同 QObject QNativeSocketEngine      更新时间:2023-10-16

我遇到了一个问题,在这里多次询问类似的情况,但是我无法在这些主题中找到解决方案。

我有一个主类,我想通过qt网络支持来扩展它,这是一个额外的类。让我为您分解源代码到相关部分:

主类

.h

class MainClass: public QObject{
    Q_OBJECT
public:
[...]
private:
    NetworkSupport * netSupport;
};

。.cpp

MainClass::MainClass()
{
    [...]
    netSupport
    netSupport = new NetworkSupport(this->thread());
    netSupport->start();
    [...]
}

网络类

.h

class NetworkSupport : public QThread
{
    Q_OBJECT
public:
    NetworkSupport(QThread *mainThread);
    ~NetworkSupport();
    QByteArray readData();
    void sendData(QByteArray *msg);
public slots:
    void acceptConnection();
    void receive();
    void run();
private:
    QByteArray curMessage;
    QThread* libThread;
    QEventLoop initWlan;
protected:
    QTcpServer server;
    QTcpSocket* client;
};

。.cpp

NetworkSupport::NetworkSupport(QThread* mainThread)
{
    libThread = mainThread;
    server.moveToThreaD(libThread);
    server.listen(QHostAddress::Any, 5200);
    QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
    initWlan.exec();
}
void NetworkSupport::run(){
    connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
void NetworkSupport::acceptConnection()
{
    client = server.nextPendingConnection();
    client->moveToThread(libThread);
    if (client->state() == QAbstractSocket::ConnectedState)
        connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
void NetworkSupport::receive()
{
    curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
    if(client->state() == QAbstractSocket::ConnectedState)
    {
        client->moveToThread(libThread);
        printf("Sending a message %sn",msg->data());
        client->write(*msg);
        client->waitForBytesWritten();
    }
}

我知道我通常不需要指定这么多moveToThread(),但如果所有这些都在那里或删除,它最终不会改变任何事情。

但是在执行时,我在sendData() client->write(*msg)收到错误消息,即:

[...]
Sending a message ?r
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QnativeSocketEngine(0x2afc660), parent's thread is QThread(0x1b59210), current thread is QThread(0x7f6a70026b60)
QSocketNotifier: Can only be used with threads started with QThread
[...]

它看起来像一个警告,因为程序之后仍在执行,但我没有收到来自客户端的任何消息(即 receive()永远不会被执行),我想这是因为错误消息的最后一行。

可能是此错误消息只是误导,如果是这样,它的实际含义是什么,还是我做错了什么?

你在这段代码中做错了很多事情,我不确定从什么开始。

NetworkSupport::NetworkSupport(QThread* mainThread)
{
    libThread = mainThread;
    server.moveToThreaD(libThread);

这将无济于事。 server 已经与 MainClass 实例位于同一线程中

    server.listen(QHostAddress::Any, 5200);

服务器将开始侦听与主类相同的线程

    QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
    initWlan.exec();

这让我很嘲笑。这将简单地启动事件循环并几乎立即退出它。

}
void NetworkSupport::run(){
    connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}

这将简单地在新线程中运行,调用连接并在连接语句后立即退出线程 - 您的线程在connect后将不再运行。此外,插槽acceptConnection可能会在与 MainClass 相同的线程中调用。我想知道何时何地创建了 MainClass。

在我看来,你同时在为很多事情而苦苦挣扎。您可能读过 somwhere,您应该使用单独的线程进行网络通信,这样您就不会阻塞其他线程。首先尝试单线程方法。当您开始工作时,您应该考虑如何利用其他线程来满足您的需求。

奖励问题:这段代码是某种可能根本没有Qt事件循环的应用程序插件吗?或者它是全栈Qt应用程序的一部分?

在你的代码中有一些误解,Qt网络和多线程是如何工作的。

首先,在Qt文档中,他们在QTcpServer::nextPendingConnection()上说:

返回的 QTcpSocket 对象不能从另一个线程使用。如果 您想使用来自另一个线程的传入连接,您需要 以覆盖传入连接()。

因此,您必须创建自己的 Server 类,该类继承自 QTcpServer,并覆盖incomingConnection()。在该重写方法中,您必须将套接字描述符发布到其他线程,TCP 客户端将在其中实时处理该连接。在这个简单的例子中,我们只是发出一个信号。

signals:
   void myNewConnectionSignal(DescriptorType);
protected:
  void incomingConnection(DescriptorType descriptor)
  {
      emit myNewConnectionSignal(descriptor);
  }

其次,NetworkSupport线程有什么用?我想,您希望您的服务器对象在那里生活和工作?如果是这样,那么您必须以其他方式重写它。以下仅为服务器部分。请注意,QThread 已经有一个 QEventLoop,你可以在 run() 中通过 exec() 使用它。

...
protected:
    MyServer *server;
...
NetworkSupport::NetworkSupport()
{
    // this is called in the main thread, so "this" lives in main thread
    server=0;
    client=0;
    // say thread to start, but it will be actually started within run()
    start();
    // sometimes it is recommended to wait here for the thread started
}
NetworkSupport::~NetworkSupport()
{
    // this is called in the main thread, say this thread to stop running
    quit();
    // wait for completion
    wait();
}
void NetworkSupport::run()
{
    // this is called in server thread
    server=new MyServer();
    if (server->listen(QHostAddress::Any, 5200))
    {
      // connect our new signal to slot where we create and init the TCP client, note that Qt::QueuedConnection is used because we want acceptConnection to run in the main thread
      connect(server, SIGNAL(myNewConnectionSignal(DescriptorType)), MyClientsPool, SLOT(acceptConnection(DescriptorType)), Qt::QueuedConnection);
      // go for infinite event loop
      exec();
    }
    else
    {
       // do you always ignore errors?
    }
    // the thread is stopped, let's destroy the server in the thread where it was born
    delete server;
    server=0;
}

客户端在您的主线程中生活和工作。不得直接从其他线程调用其方法。 NetworkSupport也存在于主线程中:是的,它封装并管理其他线程,但作为 QObject 本身,它存在于我们创建它的线程中。下面的方法总是在主线程中执行,因为我们使用 Qt::QueuedConnection 将服务器的信号连接到 NetworkSupport::acceptConnection(),它向 Qt 表示我们希望在其 QObject 所在的线程中调用该插槽。

   private slots:
      void socketDisconnected();
...
void NetworkSupport::socketDisconnected()
{
    if (client)
    {
       client->deleteLater();
       client=0;
    }
}
void NetworkSupport::acceptConnection(DescriptorType descriptor)
{
    QTcpSocket* client=new QTcpSocket(this);
    client->setSocketDescriptor(descriptor);
    connect(client,SIGNAL(disconnected(),this,SLOT(socketDisconnected());
    connect(client,SIGNAL(readyRead(),this,SLOT(receive());
}
void NetworkSupport::receive()
{
    if (client)
      curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
    if (client)
    {
        client->write(*msg);
        client->waitForBytesWritten();
    }
}

更新

如果我们只想隐藏线程内的所有网络工作。请注意,在下面的示例中,几乎没有错误处理和大量消息数据副本。您可能希望针对生产环境对其进行优化。

// private container class incapsulated and working in the thread
class NetworkThreadContainer : public QObject
{
     Q_OBJECT
     public:
        NetworkThreadContainer(QObject* parent=0):QObject(parent),client(0)
        {
           if (server.listen(QHostAddress::Any, 5200))
            {
              connect(&server, SIGNAL(newConnection()), this, acceptConnection());
            }
            else
            {
                // don't you want to throw exception here?
            }        
        }
     public slots:
         void sendDataToClient(const QByteArray& barr)
         {
               if (client)
               {
                   client->write(msg);
                   client->waitForBytesWritten();
               }
         }
        void acceptConnection()
        {
            if (!client)
            {
              client = server.nextPendingConnection();
              connect(client, SIGNAL(readyRead()), this, SLOT(receive()));            
            }
            else
            {
              // what will you do with more than one client connections or reconnections?
            }        
        }
       void NetworkSupport::receive()
       {
           if (client)
           {
              QByteArray curMessage = client->readAll();
              emit messageReceived(curMessage);
           }
       }
      signals:
         void messageReceived(const QByteArray&);
      public:
        QTcpClient* client;
        QTcpServer server;
};
// thread class visible to outer program
class NetworkThread : public QThread
{
     Q_OBJECT
     public:
        NetworkThread(QObject* parent=0):QObject(parent)
        {
           start();
        }
        ~NetworkThread()
        {
           quit();
           wait();
        }
         bool sendDataToClient(QByteArray* barr)
         {
              bool ok=false;
              // async send data to container's thread
              mutex.lock();
              if (container)
              {
                ok=true;
                QMetaObject::invokeMethod(
                    container,
                    "sendDataToClient",
                    Qt::QueuedConnection,
                    Q_ARG(QByteArray, *barr)
                );                             
              }
              else
              {
                 // container either is not ready yet or already destroyed 
              }
              mutex.unlock();
              return ok;
         }    
     signals:
        void messageReceived(const QByteArray&);
     protected:
        void run()
        {
            mutex.lock();
            // I would suggest to catch exception here and assign the error result to some variable for further processing
            container=new NetworkThreadContainer();
            mutex.unlock();
            connect(container,SIGNAL(messageReceived(QByteArray),this,SIGNAL(messageReceived(QByteArray)),Qt::QueuedConnection);
            exec();
            mutex.lock();    
            delete container;
            mutex.unlock();
        }   
     private:
        QMutex mutex;
        QPointer<NetworkThreadContainer> container;   
};