繁体   English   中英

在另一个线程中创建QNetworkAccessManager

[英]Creating QNetworkAccessManager in another thread

我在另一个线程中创建了一个QNetworkAccessManager。 该网络仅用于MyMegaThread。
QNetworkAccessManager是从线程的run方法创建的:

mp_manager.reset(new QNetworkAccessManager{this});

在创建时,我在控制台中收到这样的消息:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is MyMegaThread(0x237eabd0ee0), parent's thread is QThread(0x237e70742a0), current thread is MyMegaThread(0x237eabd0ee0)

这条消息是完全无害的,但我想知道经理应该拥有哪个父母。
我怀疑它发生是因为MyMegaThread实例是在主线程中创建的,但我需要在MyMegaThread中创建父代。

这样做的惯用方法是什么?

Parent是MyMegaThread(0x237eabd0ee0),父节点的线程是QThread(0x237e70742a0),当前线程是MyMegaThread(0x237eabd0ee0)

该问题与QNetworkAccessManager无关。

以下是重现警告的演示。

#include <QDebug>
#include <QThread>

class MyMegaThread : public QThread
{
    Q_OBJECT
public:
    using QThread::QThread;

protected:
    void run() override {
        qDebug()<<QThread::currentThread()<<this->thread();
        new QObject(this);
    }
};

// main
MyMegaThread m;
m.start();

输出:

MyMegaThread(0x60fe18) QThread(0x16a7c48)

这是QObject的规则:

所有QObject都必须与其父级位于同一个线程中。 所以:

如果涉及的两个QObject存在于不同的线程中,则setParent()将失败。 当QObject移动到另一个线程时,它的所有子节点也将自动移动。 如果QObject具有父级,则moveToThread()将失败。 如果在QThread :: run()中创建了QObject,它们就不能成为QThread对象的子节点,因为QThread不存在于调用QThread :: run()的线程中。

http://doc.qt.io/qt-5/qobject.html#thread-affinity

必须确保运行QThread此代码new QObject与给定的父QObject线程相同。

mp_manager.reset(new QNetworkAccessManager{this});

不,这条消息根本不是无害的。 您创建的对象具有空父项,并且在线程关联上没有引用,因此其thread()方法可以随时返回悬空指针。 它无法安全地使用计时器,也无法接收跨线程调用。 它基本上是无用的对象,你要求未定义的行为进行攻击。 这不应该是警告,而是失败。 Qt允许你继续,这对你有害。

这样做的惯用方法首先不是从QThread派生出来的。 QThread是一个线程句柄。 它包装了一个系统资源。 将所有功能放入常规QObject移动到QThread 在任何线程(包括主线程)上无休止地“做东西”的惯用方法是使用零持续时间计时器。 请注意,零持续时间计时器与计时完全无关。 它们本质上是事件循环句柄,称它们为计时器是用词不当。

以机智:

// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-simple-50632807
#include <QtNetwork>

class Thread final : public QThread {
   Q_OBJECT
public:
   void takeObject(QObject *obj) {
      obj->moveToThread(this);
   }
   ~Thread() override {
      requestInterruption();
      quit();
      wait();
   }
};

class Class : public QObject {
   Q_OBJECT
   QBasicTimer m_workTimer;
   QNetworkAccessManager m_manager{this};
   void doWorkChunk() {
      qDebug() << "tick...";
      QThread::sleep(1); // emulate a blocking operation
   }
protected:
   void timerEvent(QTimerEvent *ev) override {
      if (ev->timerId() != m_workTimer.timerId())
         return;
      doWorkChunk();
   }
public:
   explicit Class(QObject *parent = {}) : QObject(parent) {
      m_workTimer.start(0, this);
   }
};

int main(int argc, char *argv[]) {
   QCoreApplication app(argc, argv);
   Class object;
   Thread workThread;
   workThread.start();
   workThread.takeObject(&object);
   QTimer::singleShot(3000, &QCoreApplication::quit);
   return app.exec();
}
#include "main.moc"

QBasicTimer::stop: Failed. Possibly trying to stop from a different thread QBasicTimer::stop: Failed. Possibly trying to stop from a different thread警告QBasicTimer::stop: Failed. Possibly trying to stop from a different thread相对良性并指示内部计时器句柄泄漏。 有关解决方法,请参阅此答案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM