PDA

View Full Version : Creating a QTcpSocket or a QTcpServer in a thread other than the main thread.



jan
29th September 2009, 04:33
I have discovered that QTcpSocket and QTcpServer has to be created in main thread in order to function properly. Created in other thread results in slots not being called. Unfortunately, my part was just writing a dll which does not know which thread is creating the class. Furthermore, running the class creation in main thread should not be a requirement. Is there a way to run them reliable in other threads? I have no ground to run network activities in a UI thread.

Many Thanks!

caduel
29th September 2009, 09:41
where have you read that?

In Qt4 you can either:
* work with blocking I/O (not in the gui thread, as otherwise your app freezes til the I/O is over)
* work with non-blocking I/O: you need a QEventLoop running in your thread - you may use the main (i.e. gui) thread, but you do not have to.

HTH

piotr.dobrogost
29th September 2009, 13:40
where have you read that?

In Qt4 you can either:
* work with blocking I/O (not in the gui thread, as otherwise your app freezes til the I/O is over)
* work with non-blocking I/O: you need a QEventLoop running in your thread - you may use the main (i.e. gui) thread, but you do not have to.

HTH

Do you know how to use QNetworkAccessManager with QEventLoop other than the one in the main thread?

jan
30th September 2009, 10:16
where have you read that?

In Qt4 you can either:
* work with blocking I/O (not in the gui thread, as otherwise your app freezes til the I/O is over)
* work with non-blocking I/O: you need a QEventLoop running in your thread - you may use the main (i.e. gui) thread, but you do not have to.

HTH

Here is a sample code to illustrate the problem I have encountered. I made it as brief as possible. Hence, it is not going with the best practice. Please forgive me for that.


#include <QtNetwork/QTcpServer>
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
#include <iostream>
#include <assert.h>

class ServerThread : public QThread
{
Q_OBJECT

public:
ServerThread(QObject *ipParent)
: QThread(ipParent)
, mpServer(0)
{}

void run ()
{
mpServer = new QTcpServer();
bool lbOk(connect(mpServer, SIGNAL(newConnection()),
this, SLOT(onNewConnection())));
assert(lbOk);
mpServer->listen(QHostAddress::Any, 8888);
}

private:
QTcpServer* mpServer;

protected slots:
void onNewConnection()
{
std::cout << "new connection!" << std::endl;
}
};

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ServerThread lrThread(0);
// lrThread.start();
lrThread.run();
return a.exec();
}

It runs fine with expected outcome which print a message every time a connection is made.

However, if the line 40 is uncommented and line 41 commented, the ServerThread::onNewConnection() method is never called with connections made.

It stops working while the QTcpServer is not created on the main thread.

jan
30th September 2009, 10:17
Do you know how to use QNetworkAccessManager with QEventLoop other than the one in the main thread?

Thanks for the pointers. I will definitely look into that.:D

caduel
30th September 2009, 11:55
you need to start your QThread's event loop, see QThread::exec()

wysota
30th September 2009, 12:08
I have discovered that QTcpSocket and QTcpServer has to be created in main thread in order to function properly.
No, this is false. The socket has to have affinity with a thread with a running event loop to function properly or it needs to use the family of waitFor*() methods if not event loop is available.


Do you know how to use QNetworkAccessManager with QEventLoop other than the one in the main thread?


#include <QtGui>
#include <QtNetwork>

class Thread : public QThread {
Q_OBJECT
public:
Thread() : QThread(){ manager = 0; }
~Thread() { delete manager; }
void run(){
manager = new QNetworkAccessManager;
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*)));
QTimer::singleShot(0, this, SLOT(process()));
exec();
}
public slots:
void process(){
QNetworkReply *reply = manager->get(QNetworkRequest(QUrl("http://www.qtcentre.org/")));
QTimer::singleShot(5000, this, SLOT(process()));
}
void processReply(QNetworkReply *reply){
qDebug() << reply->size();
reply->deleteLater();
}
private:
QNetworkAccessManager *manager;
};

#include "main.moc"

int main(int argc, char **argv){
QApplication app(argc, argv);
Thread thread;
thread.moveToThread(&thread);
thread.start();
return app.exec();
}

piotr.dobrogost
30th September 2009, 19:08
Is the purpose of this code to download the same page every 5 seconds?

wysota
30th September 2009, 19:28
Yes. You wanted an example of QNetworkAccessManager working with a thread event loop, you didn't mention it should do something useful.

jan
2nd October 2009, 03:34
Thanks for your sample. You make my day!

piotr.dobrogost
2nd October 2009, 11:10
Would

connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*)), Qt::QueuedConnection);
work instead of

connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*)));
QTimer::singleShot(0, this, SLOT(process())); ?

wysota
2nd October 2009, 11:28
They are not equivalent. The timer causes the first request to be sent.

yan
5th October 2009, 22:55
Hi
are your sure this it's safe?




Thread thread;
thread.moveToThread(&thread);
thread.start();


You use moveToThread when the thread (which execute run) is not ever created
But maybe i don't understand what do moveToThread and the signification of QObject thread affinity in Qt :o)

wysota
6th October 2009, 12:48
Hi
are your sure this it's safe?
Yes.


You use moveToThread when the thread (which execute run) is not ever created
It doesn't matter, as far as I know. It's all about just marking which thread will handle events for the object and its children.

yan
6th October 2009, 23:32
It doesn't matter, as far as I know. It's all about just marking which thread will handle events for the object and its children.
Thanks for confirmation.
I investigate, and i understand all QObject have an affinity with a QThread and not the interfaced thread( that I believed), ans if you do

thread.moveToThread(&thread);
you can't call start() by signal/slot system to start a thread because the QThread eventloop don't run.

is right?

wysota
7th October 2009, 07:24
You can, but then you have to move the thread to itself after start() is executed - for example from a slot connected to the started() signal.

yan
7th October 2009, 08:17
You can, but then you have to move the thread to itself after start() is executed - for example from a slot connected to the started() signal.
ok thanks.
sorry for except subject