简体   繁体   中英

Creating QNetworkAccessManager in another thread

I have a QNetworkAccessManager created in another thread. The network is meant to be used only in MyMegaThread.
QNetworkAccessManager is created from the thread's run method:

mp_manager.reset(new QNetworkAccessManager{this});

On creation I get such a message in console:

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)

This message is totally harmless, but I wonder which parent the manager is supposed to have.
I suspect it happens because the MyMegaThread instance is created in the main thread, but I need a parent created in MyMegaThread instead.

What is an idiomatic way of doing this?

Parent is MyMegaThread(0x237eabd0ee0), parent's thread is QThread(0x237e70742a0), current thread is MyMegaThread(0x237eabd0ee0)

The issue does not relate to QNetworkAccessManager .

Here is the demo to reproduce the warning.

#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();

Output:

MyMegaThread(0x60fe18) QThread(0x16a7c48)

It's rule of QObject :

All QObjects must live in the same thread as their parent. Consequently:

setParent() will fail if the two QObjects involved live in different threads. When a QObject is moved to another thread, all its children will be automatically moved too. moveToThread() will fail if the QObject has a parent. If QObjects are created within QThread::run(), they cannot become children of the QThread object because the QThread does not live in the thread that calls QThread::run().

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

Have to make sure this code new QObject running QThread be same as given parent QObject thread.

mp_manager.reset(new QNetworkAccessManager{this});

No, the message is not harmless at all. The object you have created has a null parent and no reference on the thread association and thus its thread() method may return a dangling pointer at any time. It cannot safely use timers nor receive cross-thread calls. It is basically as useless object, and you're asking for undefined behavior to strike. This shouldn't be a warning, but a failure. Qt did you a disservice here by allowing you to continue.

The idiomatic way of doing it is first of all not to derive from QThread . QThread is a thread handle. It wraps a system resource. Put all of your functionality into a regular QObject moved into a QThread . The idiomatic way to endlessly "do stuff" on any thread, including the main thread, is to use a zero-duration timer. Note that zero-duration timers have nothing to do with timing at all. They are essentially event loop handles, calling them a timer is a misnomer.

To wit:

// 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"

The QBasicTimer::stop: Failed. Possibly trying to stop from a different thread QBasicTimer::stop: Failed. Possibly trying to stop from a different thread warning is relatively benign and indicates an internal timer handle leak. For a workaround, see this answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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