PDA

View Full Version : Questions about QThread and Signals management



StanOfSky
5th November 2010, 08:02
Hi all,

Please forget my lack of experience and knowledge about Qt.
I did look for answers in google, Qt Doc, and through this forum, but i got quite confused.

I'm currently writing a client-server application.
Communication are handled that way :
- Client connects to server and sends a request
- Server executes the request and sends back an answer
- Once the answer is fully received, client disconnects.

Some request can take some times (2-3sec) and i want the clients (Multi client) to be able to send other request while waiting for the answer.

For the time being, i'm using "Threaded Fortune Server Example" to handle multiple connections.


class TcpServer : public QTcpServer
{
Q_OBJECT
public:
TcpServer(QObject *parent);
protected:
void incomingConnection(int socket_descriptor);

void TcpServer::incomingConnection(int socket_descriptor)
{
TcpServerThread *thread = new TcpServerThread(socket_descriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}

And i'm handling communication in a synchronous approach.

void TcpServerThread::run()
{
QTcpSocket tcp_socket;

if (!tcp_socket.setSocketDescriptor(m_socket_descript or))
{
emit error(tcp_socket.error(), tcp_socket.errorString());
return;
}

/* Wait for request size.............................................. .....*/

while (tcp_socket.bytesAvailable() < (int)sizeof(qint32))
{
if (!tcp_socket.waitForReadyRead())
{
emit error(tcp_socket.error(), tcp_socket.errorString());
return;
}
}

/* Read request size.............................................. .........*/

qint32 blocksize = 0;
QDataStream in(&tcp_socket);
in.setVersion(QDataStream::Qt_4_0);
in.readRawData((char*) &blocksize, sizeof(qint32));

/* Wait until full request written on socket...............................*/

while (tcp_socket.bytesAvailable() < blocksize)
{
if (!tcp_socket.waitForReadyRead())
{
emit error(tcp_socket.error(), tcp_socket.errorString());
return;
}
}

/* Read request........................................... .................*/

char* request = (char*) malloc(blocksize);
in.readRawData(request, blocksize);

/* Execute request and build answer........................................*/

mutex.lock();
// etc ...
// return an allocated data_block
mutex.unlock();

/* Write answer to client in socket........................................*/

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);

out.writeRawData(data_block, data_size);

tcp_socket.write(block);

/* Wait until answer fully read by client..................................*/

tcp_socket.waitForDisconnected();

// free memory etc.
}
Still i'm not sure this code is safe.
I don't fully understand how QThread works.
Is m_socket_descriptor, my only member variable, shared between each instance of TcpServerThread ? Or is it allocated on the thread stack ?

I'm also using a mutex to enable only one thread at a time to "Execute request and build answer". The mutex is a member of a singleton managing the "Execute request and build answer" class/method. Is it the right way to do it ? Should the mutex be a member of TcpServerThread instead ?

On the client side, i want to be able to make several request at the same time since some request can be time-consuming and the client needs to receive continious updates from the server.
I haven't coded this part yet. This is what i want to do :
To achieve this goal, each request will be handled by a thread :

void Client::newResquest(...)
{
ClientThread *thread = new ClientThread(this, /* various parameters */);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
The run() method in ClientThread will handle comminucations just like TcpServerThread in synchronous approach.
And once the answer is fully received, ClientThread will emit a signal to a Client slot, to handle the answer. And then disconnect from server -> ClientThread is terminated.

My question is : if a ClientThread emits a signal to Client while he is already processing that signal (executing the slot connected to the signal), what happens ?
Since receiver and emmiter are in different threads, behavior is Qt::QueuedConnection ? First receiver executes slots, then second receiver waits for first one to finish executing the connected slots and then executes ?

Thank you in advance for your inputs.
I'm quite new to Qt and some answers from an advanced user would make me win some hudge amout of time :)

wysota
5th November 2010, 11:05
Is m_socket_descriptor, my only member variable, shared between each instance of TcpServerThread ? Or is it allocated on the thread stack ?
It's not allocated on the thread stack but each QThread object has its own instance of it so you can safely use it from the thread.


I'm also using a mutex to enable only one thread at a time to "Execute request and build answer". The mutex is a member of a singleton managing the "Execute request and build answer" class/method. Is it the right way to do it ? Should the mutex be a member of TcpServerThread instead ?
There is a rule regarding mutexes to protect the resouce and not the code so the mutex should only be locked when you actually reference a shared resource. Which suggests it should be locked and unlocked within the methods of your singleton class that do something with the shared resource. The threads themselves don't need to access the mutex directly.


On the client side, i want to be able to make several request at the same time since some request can be time-consuming and the client needs to receive continious updates from the server.
I haven't coded this part yet. This is what i want to do :
To achieve this goal, each request will be handled by a thread :

I really hate when threads are used for networking. The ideal architecture for both the server and the client would be that networking is handled in the main thread and only the task of preparing a response for the client on the server side would be done in a thread as this assures a good use of resources. The client doesn't need threads at all, if you only perform one request to a server per connection, you can handle it all asynchronously in a clear way.


My question is : if a ClientThread emits a signal to Client while he is already processing that signal (executing the slot connected to the signal), what happens ?
The signal is queued and when the main thread finishes executing the slot and comes back to the event loop, eventually the pending signal will get processed.

StanOfSky
5th November 2010, 18:12
Thank you very much for the quick answer :)


It's not allocated on the thread stack but each QThread object has its own instance of it so you can safely use it from the thread.

Ok that's what i thought. But unless i was sure of that, i could'nt be sure my code was doing what it's supposed to.


There is a rule regarding mutexes to protect the resouce and not the code so the mutex should only be locked when you actually reference a shared resource. Which suggests it should be locked and unlocked within the methods of your singleton class that do something with the shared resource. The threads themselves don't need to access the mutex directly.

Well that's the way i always used mutex, but i was not sure of the way Qt handled them ;)
And since my singleton is only used in that thread.


I really hate when threads are used for networking. The ideal architecture for both the server and the client would be that networking is handled in the main thread and only the task of preparing a response for the client on the server side would be done in a thread as this assures a good use of resources. The client doesn't need threads at all, if you only perform one request to a server per connection, you can handle it all asynchronously in a clear way.

I see what you mean.
But i won't have that much client and connexions (i guess 10 at same time will be max).
And it was really more clear in my head how to handle them in a synchronous manner ;)
That way, i'm pretty much confident that my code is bug free even if it's not the best way to do it.


The signal is queued and when the main thread finishes executing the slot and comes back to the event loop, eventually the pending signal will get processed.

That's what i thought too. But as for QThread, if i didn't know that, i could'nt be sure my code was doing what it's supposed to.


Thank you again.
Stan

tbscope
5th November 2010, 18:25
it was really more clear in my head how to handle them in a synchronous manner ;)
I understand it's clear when you're dealing with one and only one connection at the same time.
But boy does it get complex when you need to deal with multiple connections at once.


That way, i'm pretty much confident that my code is bug free even if it's not the best way to do it.
Want to take a bet that if you write a multithreaded client/server program I'm going to point out at least one bug? :-)

Here's some inspiration:
http://www.qtcentre.org/wiki/index.php?title=Multi_client_server_without_thread ing

StanOfSky
6th November 2010, 02:27
I understand it's clear when you're dealing with one and only one connection at the same time.
But boy does it get complex when you need to deal with multiple connections at once.


Want to take a bet that if you write a multithreaded client/server program I'm going to point out at least one bug? :-)

Here's some inspiration:
http://www.qtcentre.org/wiki/index.php?title=Multi_client_server_without_thread ing

I did read http://www.qtcentre.org/wiki/index.php?title=Multi_client_server_without_thread ing before asking.
But considering the way communication need to be handled (in my case), that example was not really better suited.
My clients don't stay connected, they just request and disconnect (and at most i can get 10 requests (clients) at the same time)
I would still need to create a thread to process the request if more than one client connect at the same time (because i don't want an heavy computation time request to penalize other clients)

The only difference between the way i handle this, and http://www.qtcentre.org/wiki/index.php?title=Multi_client_server_without_thread ing do, is that my thread includes Tcp communications.

For a server for which each client stay connected all along, separating communication from threaded request/answer process makes sense.


And i find easier to debug that way ;)

tbscope
6th November 2010, 06:11
1. Threads do not make code run faster
2. Threads do not make your code read easier
3. Threads do not mean that tasks can be run in parallel
4. Threads do not mean that you can make code easier.

In fact, the opposite is true.
Threads are used to relieve the main program event loop from blocking when doing cpu intensive tasks. I would agree in using a thread for computing something very cpu intensive, like the number pi to a 1000 digits.

But socket communication is one item that does not benefit from threading.
There's only one reason to use threads and sockets together and that is when you absolutely need to use a synchronous connection (as in, a device or protocol needs you to do that).
But a TCP connection does not benefit from threading.

In fact, your threads stay 99,9999% of the time idle, doing nothing. That's just wasting system resources. Do you know how much memory one thread consumes?
Any TCP connection can easily be handled by the main event loop. Just don't block the main event loop and make use of it.

Threads also don't help you with reconnecting a closed connection. How would it do that?

StanOfSky
6th November 2010, 07:18
1. Threads do not make code run faster
2. Threads do not make your code read easier
3. Threads do not mean that tasks can be run in parallel
4. Threads do not mean that you can make code easier.
Thank you, i'm fully aware of that ;)

In fact, the opposite is true.
Threads are used to relieve the main program event loop from blocking when doing cpu intensive tasks. I would agree in using a thread for computing something very cpu intensive, like the number pi to a 1000 digits.
Well that's quite the point of my threads, they handle the intensive computations of request/answer. They are not needed to handle tcp communication.
When i first start coding, i was not aware of the "qobject_cast<QTcpSocket *>(sender());" trick.
I also didn't know how signal manage multiple calls at the same time.
If i did, i would have used the asynchronous way, but still i would have make a QThread in my tcpSocket readyRead slot, to handle the heavy computations done by the server.

In fact, your threads stay 99,9999% of the time idle, doing nothing. That's just wasting system resources. Do you know how much memory one thread consumes?
In fact, when they idle, they don't waste that much, they are just queued waiting for the right signal.
What's really wasting system resources is context switch, at least for process, a bit less for threads.
Anyway, you are right, i should exclude tcp communication from the thread, i might do it now that i have a better understanding of Qt mechanisms ;)

wysota
6th November 2010, 09:31
They are not needed to handle tcp communication.
But still they handle tcp communication.


In fact, when they idle, they don't waste that much, they are just queued waiting for the right signal.
In fact they do waste memory of both the process and the system regardless of their state. And when they become active from time to time (something has to periodically check whether there is something waiting in the socket, right?), they do waste cpu power for context switching. For ten threads you get ten separate select() (or equivalent) calls each checking one descriptor. For a single thread this can all be handled by one select() (or equivalent) call.


What's really wasting system resources is context switch, at least for process, a bit less for threads.
You are context switching between proceses - always, regardless of using threads. Remember there is more than one process running in your system. Unless you have more processing units than processes (and all their threads), context switching will always be involved. And if you access a shared resource somewhere in the process, your threaded process will slow down because of that fact when under heavy stress.

At some point you said you handle at most 10 clients concurrently. How do you assure that only 10 clients can connect at a time?

StanOfSky
6th November 2010, 13:44
At some point you said you handle at most 10 clients concurrently. How do you assure that only 10 clients can connect at a time?

I can't assure that, it's an estimation based on the client's behavior, the time needed to handle a request, and the number of clients requesting at the same time.
Most of the time i'll get 2-3 threads max. But in some extreme case, i could go up to 10 threads.


But still they handle tcp communication.

In fact they do waste memory of both the process and the system regardless of their state. And when they become active from time to time (something has to periodically check whether there is something waiting in the socket, right?), they do waste cpu power for context switching. For ten threads you get ten separate select() (or equivalent) calls each checking one descriptor. For a single thread this can all be handled by one select() (or equivalent) call.

I will port a asynchronous. I'm aware of those issues, even if with 10 max threads (3 most of the time), that won't change much.

But i still have some questions to achieve that.
I have to create a new QThread in readyRead slot, pass him the socket involved. Should i :
- pass the socketDescriptor (int) and create a socket in the thread stack with that descriptor : whats happens to the socket when thread finishs and objects are destroyed ?
- pass a pointer to that socket (QTcpSocket*) and then call a method of an object allocated in another thread (main thread) ?
since i need to write to the socket in that thread.

wysota
6th November 2010, 15:36
I can't assure that, it's an estimation based on the client's behavior, the time needed to handle a request, and the number of clients requesting at the same time.
Most of the time i'll get 2-3 threads max. But in some extreme case, i could go up to 10 threads.
I can make your program to (try to) go up to 10000 threads (and crash) within seconds.



I have to create a new QThread in readyRead slot,
Hmm? What for?