QObject 无法为处于不同线程中的父对象创建子对象

QObject Cannot create children for a parent that is in a different thread

您好,我需要从后台进行一些套接字通信,我对此使用了 QtConcurrent::run,但给了我警告。

QObject: Cannot create children for a parent that is in a different thread.
(Parent is MainWindow(0x7fff3e69f500), parent's thread is QThread(0x16f8070), current thread is QThread(0x17413d0)

这是代码,

MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),ui(new Ui::MainWindow){
      ui->setupUi(this);
      QFuture<void> f2 = QtConcurrent::run(this,&MainWindow::checkCamStatus);
  }


 void MainWindow::checkCamStatus(){
     while(1){
       bool flag = false;
       QTcpSocket* socket = new QTcpSocket(this);
       socket->moveToThread(this->thread());
       socket->setParent(this);
       socket->connectToHost("10.0.7.112", 80);
       if(socket->waitForConnected(1000))//check for connection for i second
       {
         qDebug() << "Cam online";
       }
       else{
         qDebug() << "..............................Cam offline";
       }

       QThread::sleep(1);
     }
 }

如何删除警告?

QFuture<void> f2 = QtConcurrent::run(this,&MainWindow::checkCamStatus);

QFuture 在不同的线程上运行 &MainWindow::checkCamStatus 函数。

当您在 checkCamStatus 函数中创建新套接字时,它发生在不同的线程上。您应该在 checkCamStatus 函数之外创建套接字。

或者,您可以修改现有代码以避免像这样的完全重构:

QTcpSocket* socket = new QTcpSocket(); // Remove the parent from here
socket->moveToThread(this->thread());  // Move socket back to the original thread
socket->setParent(this);               // Set socket's parent here, this and socket are now on the same thread

但是这个替代解决方案不太优雅。

首先,你的代码有什么问题:

您不能在 new QTcpSocket(this) 中使用 this,因为 this->thread() 不是当前线程 (QThread::currentThread())。

您不能在 socket->moveToThread(this->thread()) 之后调用 socket 的任何函数成员,因为 socket->thead() 不再是当前线程。

这已记录在案 here:

Event driven objects may only be used in a single thread. Specifically, this applies to the timer mechanism and the network module. For example, you cannot start a timer or connect a socket in a thread that is not the object's thread.

现在,如果您只想连接一个 TCP 套接字而什么都不做,您可以做的是修复您的代码,就是删除对 this 的任何引用并执行类似的操作:

void MainWindow::checkCamStatus(){
     while(1){
       bool flag = false;
       QScopedPointer<QTcpSocket> socket(new QTcpSocket()); 
       socket->connectToHost("10.0.7.112", 80);
       if(socket->waitForConnected(1000))//check for connection for i second
       {
         qDebug() << "Cam online";
       }
       else{
         qDebug() << "..............................Cam offline";
       }

       QThread::sleep(1);
     }
 }

但是,如果你想用 TCP 套接字做其他事情,比如发送或接收数据,你最好将你的代码包装在 class:

class CamWatcher : public QObject
{
    Q_OBJECT
public:
    CamWatcher(QObject *parent = 0) :
        QObject(parent),
        m_socket(new QTcpSocket(this))
    {}
    QTcpSocket *socket() { return m_socket; }

public slots:
    void tryConnect() { socket->connectToHost("10.0.7.112", 80); }

private slots:
    void onSocketConnected() { qDebug() << "Connected"; }
    void onSocketDisconnected()  { qDebug() << "Disconnected"; }
    void onSocketError(QAbstractSocket::SocketError socketError)
    {
        qWarning() << m_socket->errorString();
    }
private:
    QTcpSocket *m_socket = nullptr;
}

这样您就可以将所有 TCP 内容(监控、数据传输等)放入一个对象中。您可以将它与 MainWindow 放在同一个线程中,但如果需要,您也可以将它放在另一个线程中。

请注意,如果您在 MainWindow 之外的另一个线程中有 CamWatcher,但您想要调用 CamWatcher 的函数,则必须执行如下操作:

QTimer::singleshot(0, camwatcher, &CamWatcher::tryConnect);
QMetaObject::invokeMethod(camwatcher, "tryConnect", Qt::QueuedConnection); // No compile time check, works only with slots
QMetaObject::invokeMethod(camwatcher, &CamWatcher::tryConnect, Qt::QueuedConnection); // Requires Qt 5.10