[SOLVED] A problem about QNetworkAccessManager, QThread and Event Loop
-
Hi there.
I have a problem about QNetworkAccessManager, QThread and Event Loop. I want to do a HTTP request periodically in a new thread. Since QNetworkAccessManager needs the event loop, I have to use QThread::exec() in the run() of the thread. But in this way, I cannot do the periodical request. Here are my codes:void DIThread::replyFinished(QNetworkReply *reply) { qDebug() << "Here"; QTextCodec *codec = QTextCodec::codecForName("UTF-8"); QString all = codec->toUnicode(reply->readAll()); qDebug() << all; reply->deleteLater(); sleep(1); manager->get(QNetworkRequest(QUrl("http://www.baidu.com"))); } void DIThread::run() { manager = new QNetworkAccessManager(); connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); manager->get(QNetworkRequest(QUrl("http://www.baidu.com"))); QThread::exec(); }
the function manager->get(QNetworkRequest(QUrl("http://www.baidu.com"))); will execute one time, and errors will appear in the next time:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x13d9c98), parent's thread is DIThread(0x12fb70), current thread is QThread(0x4aeab0)
So, how to fix this problem? -
Why do you call QThread::exec() in run()?
Check the example in http://doc.qt.io/qt-5.5/qthread.html -
@jsulm Thanks for your reply.
If don't call exec(), the slot will not work. -
@sheeley said:
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
This is the cause of your problem.
See the documentation that @jsulm posted:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
When subclassing QThread, keep in mind that the constructor executes in the old thread while run() executes in the new thread.
The problem is,
run()
is called in the new thread, butreplyFinished()
is called in the old thread.run()
constructs a QNetworkAccessManager in the new thread, and callsQNetworkAccessManager::get()
in the new thread. This is OK.replyFinished()
callsQNetworkAccessManager::get()
in the old thread. This is not allowed.
Do not add a slot to
DIThread
. Use a lambda function to handle the signal:connect(manager, &QNetworkAccessManager::finished, [=](QNetworkReply *reply) { qDebug() << reply->readAll().toUtf8(); // You don't need QTextCodec! reply->deleteLater(); sleep(1); manager->get(QNetworkRequest(QUrl("http://www.baidu.com"))); });
-
@JKSH Thanks very much! It works! I'll check the documentation for more details. Thanks again.