PDA

View Full Version : Closing QThread with QTcpSocket



eekhoorn12
16th June 2010, 19:46
Hey All

I am writing a program that uses QTcpSocket to send data, because i want multiple connections at the same time and keep the application responsive i want to use QThread to send the data in the background.

Before I want to implement this in my program i was trying to create a small test program to test everything and learn how it works. But i keep getting the same problem every time.

When i close the application i get a warning saying:

QWaitCondition: Destroyed while threads are still waiting

I have search these forums and other forums for a solution but couldn't find the right one.
A few days ago i found this post http://www.qtcentre.org/forum/f-qt-programming-2/t-non-gui-thread-blocking-gui-3848.html#14

But when i add a QTcpSocket to the object created in the thread and close the application i get the error.

This is the code i have right now:

#include <QtGui>
#include <QtDebug>
#include <QTcpSocket>

class TestObject : public QObject
{
Q_OBJECT

public:
TestObject(QObject* parent = 0) : QObject(parent)
{
testSocket = new QTcpSocket();
testSocket->connectToHost("www.google.com",80);
}

private:
QTcpSocket *testSocket;

public slots:
void testObjectSlot()
{
qDebug() << "TestObject::testObjectSlot()" << QThread::currentThread();
}

void exitObjectSlot()
{
qDebug() << "TestObject::exitObjectSlot()" << QThread::currentThread();
if(testSocket != NULL){
testSocket->disconnectFromHost();
if (testSocket->state() == QAbstractSocket::UnconnectedState ||
testSocket->waitForDisconnected(1000))
qDebug("Socket is disconnected!");
testSocket->deleteLater();
testSocket = NULL;
}
}

};

///////////////////////////////////////////////////////////////////////////////
class TestThread : public QThread
{
Q_OBJECT

public:
TestThread(QObject* parent = 0) : QThread(parent)
{
start();
}

~TestThread()
{
qDebug() << "TestThread::~TestThread()" << QThread::currentThread();
quit();
wait();
}

public slots:
void testThreadSlot()
{
qDebug() << "TestThread::testThreadSlot()" << QThread::currentThread();
}

signals:
void testObjectSignal();
void exitObjectSignal();

protected:
void run()
{
TestObject object;
connect(this, SIGNAL(testObjectSignal()), &object, SLOT(testObjectSlot()));
connect(this, SIGNAL(exitObjectSignal()), &object, SLOT(exitObjectSlot()));
exec();
}
};

///////////////////////////////////////////////////////////////////////////////
class TestWidget : public QWidget
{
public:
TestWidget(QWidget* parent = 0) : QWidget(parent)
{
QPushButton* button1 = new QPushButton("Test QThread directly", this);
connect(button1, SIGNAL(clicked()), &thread, SLOT(testThreadSlot()));

QPushButton* button2 = new QPushButton("Test QObject created in QThread::run()", this);
connect(button2, SIGNAL(clicked()), &thread, SIGNAL(testObjectSignal()));

QPushButton* button3 = new QPushButton("Stop Thread", this);
connect(button3, SIGNAL(clicked()), &thread, SIGNAL(exitObjectSignal()));

QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
setLayout(layout);
}

~TestWidget()
{
}

private:
TestThread thread;
};

///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
qDebug() << "main()" << QThread::currentThread();
TestWidget w;
w.show();
return a.exec();
}

#include "main.moc"


Hope you guys can help me

Marcel

tbscope
16th June 2010, 20:02
I am writing a program that uses QTcpSocket to send data, because i want multiple connections at the same time and keep the application responsive i want to use QThread to send the data in the background.

That's not correct. You can use multiple sockets and send and receive data without blocking the user interface.
Take a look here: http://www.qtcentre.org/wiki/index.php?title=Server_with_multiple_clients_witho ut_threads
Not using threads will make your code a lot easier.



~TestThread()
{
qDebug() << "TestThread::~TestThread()" << QThread::currentThread();
quit();
wait();
}
There's your wait.




void run()
{
TestObject object;
connect(this, SIGNAL(testObjectSignal()), &object, SLOT(testObjectSlot()));
connect(this, SIGNAL(exitObjectSignal()), &object, SLOT(exitObjectSlot()));
exec();
}

The creation of the object called "object" is very wrong for what you want to do. It will go out of scope.

And since you start an event loop, it would be nice to treat the event loop a little bit nicer. In plain English, you should exit or quit your event loop before closing your main program.

eekhoorn12
16th June 2010, 20:41
That's not correct. You can use multiple sockets and send and receive data without blocking the user interface.
Take a look here: http://www.qtcentre.org/wiki/index.php?title=Server_with_multiple_clients_witho ut_threads
Not using threads will make your code a lot easier.

When i look at that code it is used to send small messages, but will this also work when i want 8 connections sending 4 gig of data at my max upload speed?



There's your wait.


The creation of the object called "object" is very wrong for what you want to do. It will go out of scope.

And since you start an event loop, it would be nice to treat the event loop a little bit nicer. In plain English, you should exit or quit your event loop before closing your main program.


I changed to code a bit and it now looks like this:

#include <QtGui>
#include <QtDebug>
#include <QTcpSocket>

class TestObject : public QObject
{
Q_OBJECT

public:
TestObject(QObject* parent = 0) : QObject(parent)
{
testSocket = new QTcpSocket();
testSocket->connectToHost("www.google.com",80);
}

private:
QTcpSocket *testSocket;

public slots:
void testObjectSlot()
{
qDebug() << "TestObject::testObjectSlot()" << QThread::currentThread();
}

void exitObjectSlot()
{
qDebug() << "TestObject::exitObjectSlot()" << QThread::currentThread();
if(testSocket != NULL){
testSocket->disconnectFromHost();
if (testSocket->state() == QAbstractSocket::UnconnectedState ||
testSocket->waitForDisconnected(1000))
qDebug("Socket is disconnected!");
testSocket->deleteLater();
testSocket = NULL;
}
}

};

///////////////////////////////////////////////////////////////////////////////
class TestThread : public QThread
{
Q_OBJECT

public:
TestThread(QObject* parent = 0) : QThread(parent)
{
start();
}

~TestThread()
{
qDebug() << "TestThread::~TestThread()" << QThread::currentThread();
exit(0);
wait();
}

public slots:
void testThreadSlot()
{
qDebug() << "TestThread::testThreadSlot()" << QThread::currentThread();
}

private:
TestObject *object;

signals:
void testObjectSignal();
void exitObjectSignal();

protected:
void run()
{
qDebug() << "TestThread::run()" << QThread::currentThread();
object = new TestObject;
connect(this, SIGNAL(testObjectSignal()), object, SLOT(testObjectSlot()));
connect(this, SIGNAL(exitObjectSignal()), object, SLOT(exitObjectSlot()));
exec();
}
};

///////////////////////////////////////////////////////////////////////////////
class TestWidget : public QWidget
{
public:
TestWidget(QWidget* parent = 0) : QWidget(parent)
{
thread = new TestThread();
QPushButton* button1 = new QPushButton("Test QThread directly", this);
connect(button1, SIGNAL(clicked()), thread, SLOT(testThreadSlot()));

QPushButton* button2 = new QPushButton("Test QObject created in QThread::run()", this);
connect(button2, SIGNAL(clicked()), thread, SIGNAL(testObjectSignal()));

QPushButton* button3 = new QPushButton("Stop Thread", this);
connect(button3, SIGNAL(clicked()), thread, SIGNAL(exitObjectSignal()));

QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
setLayout(layout);
}

~TestWidget()
{
qDebug() << "TestWidget::~TestWidget()" << QThread::currentThread();
thread->~TestThread();
thread->wait();
}

private:
TestThread *thread;
};

///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
qDebug() << "main()" << QThread::currentThread();
TestWidget w;
w.show();
return a.exec();
}

#include "main.moc"


This, i think, should solve the problem of the object going out of scope.

But i thought that calling quit() in ~TestThread() would end the event loop just as the documentation says:

Each QThread can have its own event loop. You can start the event loop by calling exec(); you can stop it by calling exit() or quit().

Which it apparently doesn't.

So how do i stop the event loop. Is this the problem that the event loop is living in another thread than the thread from which i call quit().

tbscope
16th June 2010, 20:51
When i look at that code it is used to send small messages, but will this also work when i want 8 connections sending 4 gig of data at my max upload speed?
It depends. If you receive and send the data in packets, and this is what happens if you use TCP, then yes, this works without any problem.
Your bottleneck is not your cpu, it has plenty of power to handle all that data. The bottleneck is your hard disk, especially when your network speed is very high. That's why webservers do not require cpu speed but tons of fast memory.

The only reason to use threads if you want to do something cpu intensive like doing something with the data, and even then you don't need a thread for each connection.

However, using threads will work very well and I'm not trying to stop you from using them. I only want to give you an option to make it yourself a lot easier.




~TestWidget()
{
qDebug() << "TestWidget::~TestWidget()" << QThread::currentThread();
thread->~TestThread();
thread->wait();
}


Am I correct when I say that you do not have a lot of experience with programming in C++ ?
You do not call "thread->~TestThread();" directly. You call "delete thread;"
Even more wrong is using it after you delete it.

Instead of deleting the thread right away, why not call thread->exit() or quit() first?

eekhoorn12
16th June 2010, 21:32
Am I correct when I say that you do not have a lot of experience with programming in C++ ?

You are right. I have experience with programming but the stuff i have done so far is basic school stuff. This is why i am doing this project to learn from it.



You do not call "thread->~TestThread();" directly. You call "delete thread;"
Even more wrong is using it after you delete it.

Instead of deleting the thread right away, why not call thread->exit() or quit() first?

When i change the code to

~TestWidget()
{
qDebug() << "TestWidget::~TestWidget()" << QThread::currentThread();
thread->exit();
thread->wait();
}

I still get the error but i expect the program to wait at thread->wait(); until the thread is finished.

wysota
17th June 2010, 03:22
When i look at that code it is used to send small messages, but will this also work when i want 8 connections sending 4 gig of data at my max upload speed?

Do you have about 16 processors in your machine? If not, then threads won't help you, they will only slow down your application instead of speeding it up.