PDA

View Full Version : WaitForEvent



svenni
14th March 2006, 10:59
Hi all!

Is there a (clean) way for the gui thread to wait for a thread to finish while staying responsible for user interaction?

A dirty way would be to loop over QApplication::processEvents, like

while (!thread.finished())
{
QApplication::processEvents();
}

, but this will burn the gui thread even if there is no user interaction.

I would be nice to have something like "waitForEvent", so that gui events could wake up the gui thread.

vratojr
14th March 2006, 11:34
Hrmmm,once you've called UserThread::start() it's done.
The thread exits from run once it has finished and in the meanwhile your gui is still active.
What do you want to do?
You should not use the thread to create a GUI.
You have to keep the GUI in the principal thread:
From the Qt's doc:
"Note that QCoreApplication::exec() must always be called from the main thread (the thread that executes main()), not from a QThread. In GUI applications, the main thread is also called the GUI thread because it's the only thread that is allowed to perform GUI-related operations."

svenni
14th March 2006, 12:05
Thanks for the answer.

The situation is as follows:
The application should execute commands from a command list (something like a command script). The commands from the script must be executed one after the other, because they depend on each others data, so from the main (gui) thread, I call:

Execute command 1 from script
wait for command 1 to finish, update GUI, etc...
Execute command 2 from script
wait for command 2 to finish, update GUI, etc...
and so on...

but while the application waits for the script command to finish, the user should be able to resize the application window or whatever. So, this "wait for the command to finish" should not block user input.

wysota
14th March 2006, 12:10
Use event driven approach. QThread can emit a signal when it's done. You can connect it to a slot, where you'll start your second job which will emit a signal when it's done and if you connect to it, you can trigger doing more stuff... All the time the gui remains active, as nothing blocks the event queue.

vratojr
14th March 2006, 12:16
Hrmm,you could create a thread that acts this way.


QThread::run(){
Execute_command1(...)
Execute_command2(..)
and so on...
}


From the GUI thread you call QThread::start() and let the thread do his homeworks and in the meanwhile your GUI will still be responsive.
See the Mandelbrot example in the doc, I think you want to do something like that ;).

svenni
14th March 2006, 13:50
Thanks again. I think the real reason for my posting is bad application design. But I can still think of a use case for waitForEvent() ...

Think of a situation like this:
Assuming my class has three methods: methodA, methodB and methodC. methodsB and methodC depend on data that is created by methodA.

In my code I have calls like

{
methodA();
methodB();
}

{
methodA();
methodC();
}

in many, many places all over the aplication.

Now I decide to have methodA to do its work in a thread, because methodA works slower than expected, and the gui should stay responsive while methodA is at work. But I must be careful, because methods B and C have to wait for methodA to finish.

One possibility would be to react on the finished() signal as proposed, but why should methodB know that methodA does its homework in a thread? It should not be of any interest for methodB how methodA does it's work.

Another possibility would be to create a thread that executes both methodA+B, but what if methodB accesses the gui? It can't be run in a thread other than the gui thread.

In that situation, all I want to do in methodA is

methodA
{
-) startThread, that computes the maximum prime (whatever)
-) wait for this thread to finish, but stay responsive for gui events (e.g. move the window)
-) exit methodA
}

So the "thread work" would be fully enclosed in method A, no other method would have to care about how methodA does its work.

I don't have to change all sequences where I call
{
methodA();
methodB();
}


In this situation, a "waitForEvent" would be an easy way to keep the application responsive for user input while keep call sequences (and dependencies)...

Once again, perhaps these ideas are only driven by bad application design...

Thanks for your help!

vratojr
14th March 2006, 14:15
Think of a situation like this:
[...]
Now I decide to have methodA to do its work in a thread, because methodA works slower than expected, and the gui should stay responsive while methodA is at work. But I must be careful, because methods B and C have to wait for methodA to finish.

One possibility would be to react on the finished() signal as proposed, but why should methodB know that methodA does its homework in a thread? It should not be of any interest for methodB how methodA does it's work.
Well,I confess I haven't well understood what you want to say in the last paragraph but if you use the SIGNAL/SLOT method to start methodB after the end of the thread that computes methodA..well methodB is not aware of the fact that mA is running in a thread:mB runs in the principal thread and it starts as the thread emits the finished signal.


Another possibility would be to create a thread that executes both methodA+B, but what if methodB accesses the gui? It can't be run in a thread other than the gui thread.

You can make you thread communicate with the GUI thread using signals.



In that situation, all I want to do in methodA is

methodA
{
-) startThread, that computes the maximum prime (whatever)
-) wait for this thread to finish, but stay responsive for gui events (e.g. move the window)
-) exit methodA
}

This is exactly what you can achieve using slot connection.

You are welcome.

wysota
14th March 2006, 14:16
One possibility would be to react on the finished() signal as proposed, but why should methodB know that methodA does its homework in a thread? It should not be of any interest for methodB how methodA does it's work.
It doesn't have to know that. If you provide "start()" slot for it, you can just connect some signal to it to make it work. It doesn't matter if the signal is finished() signal from QThread of clicked() signal from a push button -- it doesn't care, because the connection is made from some external part of the code, which knows if your job should be triggered by end of another thread, click of a button or anything else.



Another possibility would be to create a thread that executes both methodA+B, but what if methodB accesses the gui? It can't be run in a thread other than the gui thread.

It can, but it must always use custom events (or signals) to send "comands" to the gui, no matter if it uses threads or not.


In that situation, all I want to do in methodA is

methodA
{
-) startThread, that computes the maximum prime (whatever)
-) wait for this thread to finish, but stay responsive for gui events (e.g. move the window)
-) exit methodA
}

So the "thread work" would be fully enclosed in method A, no other method would have to care about how methodA does its work.

I don't have to change all sequences where I call
{
methodA();
methodB();
}


In this situation, a "waitForEvent" would be an easy way to keep the application responsive for user input while keep call sequences (and dependencies)...

Once again, perhaps these ideas are only driven by bad application design...

Thanks for your help!

Maybe it would be better not to use separate threads for that?

You gave an example of calculating a prime number, so let's stick with that.
Calculating prime numbers consists of some steps, let's use a trivial algorithm for that:

int findNextPrime(int startFrom){
while(1){
if(isPrime(startFrom)){
return startFrom;
} else {
++startFrom;
}
}
}
Now this runs in a loop which would make gui unresponsive. But what happens if you change it to:

int findNextPrime(int startFrom){
while(1){
if(isPrime(startFrom)){
return startFrom;
} else {
++startFrom;
}
qApp->processEvents();
}
}

Now we have a responsive GUI, because upon each iteration, some gui events are handled. But let's go further...


int candidate;
void findNextPrime(int startFrom){
candidate = startFrom;
tryPrime();
}

void tryPrime(){
if(isPrime(candidate)){
emit primeFound(candidate);
return;
}
++candidate;
QTimer::singleShot(0, this, SLOT(tryPrime())); // you can change 0 to something larger to slown down the process
}

Now what we have here is the proper way of doing time intensive operations without using hacks like QApplication::processEvents(). You start a process of calculating a prime number and then order a function to be called every time there are no events to process (this is the "0" in QTimer::singleShot) which does some part of the job and schedules itself for execution again if it didn't complete its job yet (otherwise it just informs its environment that it found a result by emitting a signal containing the result).

So the bottom line is -- if you can divide your job into "steps", you don't have to use separate threads (unless you have more than one CPU in your computer using threads for time consuming operations doesn't make much sense anyway).

Elder Orb
15th March 2006, 12:25
Thank you, wysota! Until now I used
QApplication::processEvents in time-consumpting operations. It seems like using QTimer is really more proper way!. But I can't understand how to apply new approach in the next sitiuation:
I have AsciiProtocol class, working with any QIODevice, which has a method waitForCmd. Inside this method I did something like



bool AsciiProtocol::waitForCmd() {
forever {
if (.. received proper cmd ? )
break;

QApplication::processEvents();
}
}


So if waitForCmd is called from gui thread it will return only if proper ascii command will come (or in the case of error or exception) but gui stays responsible. Can such behaviour of waitForCmd be aquired using QTimer::singleShot approch ?

wysota
15th March 2006, 12:59
You can use signal-slot connection for that. There is no need for timers here. When some events in the device happens, emit a signal, catch it and check if this is the command you want. If so, emit another signal instead of waiting for something to happen in a loop. Instead of calling a blocking command (waitForCmd) just connect a signal to a slot which will continue the algorithm when the event happens.

Elder Orb
15th March 2006, 20:30
Ok, maybe. But how to rewrite the next algorithm with signals and slots?

gui thread:



startDataExchange() {

// create protocol, using customIODevice
AsciiProtocol protocol(customIODevice);

// Ascii protocol's method sending some data into QIODevive
protocol.sendCmd("cmd1;");

// wait for answer
if(!waitForCmd())
errorHandler();

if(protocol.getCmd() == "someCommand") {
.. some actions ..
} else if ..... {

} else {

}

protocol.sendCmd("cmd2;");

... etc..
}


If I understood right, all I need is to do something following:



init() {
// attach protocol to customIODevice
protocol->attach(customIODevice);
}

startDataExchange() {
connect(protocol, SIGNAL(cmdReceived()), this, SLOT(handleCmd1()));

// Ascii protocol's method sending some data into QIODevive
protocol.sendCmd("cmd1;");
}

handleCmd1() {
// cmd1 received ?
if(protocol->cmd() == "cmd1") {

disconnect(protocol, SIGNAL(cmdReceived()), this, SLOT(handleCmd1()));
connect(protocol, SIGNAL(cmdReceived()), this, SLOT(handleCmd2()));
protocol->sendCmd("cmd2");
}
}

handleCmd2() {
... etc
}


But to my mind it is not such a clear approach, as if all the data exchange related code situated in one function... What will be if data exchange session has 10 data senders and 10 data receivers? And what will be if this number is unknown before session starts ? QApplication :: processEvents worked for me in this way, but it used too much CPU time, so I was need to put Sleep(1) before it (win32 api functuion).. That is why I thought QTimer maybe is more helpful in my case...

wysota
15th March 2006, 21:01
In your case I would implement something like a state machine.


init() {
// attach protocol to customIODevice
protocol->attach(customIODevice);
}

void someclass::someMethod(){
connect(protocol, SIGNAL(cmdReceived(commandObject &)), this, SLOT(handleCmd(commandObject &)));
protocol.sendCmd("cmd1;");
}

void someClass:handleCmd( commandObject &obj){
switch(obj->type()){
case Cmd1: //...
prevcommands.push_back("cmd1");
protocol.sendCmd("cmd2;");
break;
case Cmd2: //...
prevcommands.push_back("cmd2");
protocol.sendCmd("cmd3;");
break;
case Cmd3: //...
if(prevcommands[0]=="cmd1" && prevcommands[1]=="cmd2"){
protocol.sendCmd("cmd1");
} prevcommands.clear();
break;
//...
}
}

handleCmd slot implements the state machine. The one above is just an example. The idea is to cover all possible situations (states) and implement solutions for them. For example if you receive cmd1 and then cmd2, you should send cmd3. If you receive cmd1, cmd2 and cmd3, you should forget about all the commands already received and start from the beginning. etc.

Elder Orb
16th March 2006, 09:40
Thank you for great idea! I'll try it!