PDA

View Full Version : Some QThread questions



justoit
19th May 2011, 17:17
Hi everyone,

I have a computation that takes some time (let's say 200ms) and has to be executed all the time during program execution. Basically it takes some user input, does its 200ms work on it, delivers a result and instantly starts over again with the input data that may or may not have changed in the meantime. So I checked the Qt doc, put that stuff in a QThread subclass with it's run method looking basically like this:


void myThread::run()
{
initSomeStuff();
while(true)
{
performLongTask();
}
}

This means that the thread is basically busy all day with minimal interaction with the rest of the process, which should be fine as it's a worker thread. But after I start that thread it's slowing the responsibility my GUI op to the point of rendering it almost static - can anyone tell me what I missed? I checked the doc as well as some blogs and discussions including the often quoted "you're doing it wrong" article but ended up even more unsure about the proper approach. Maybe one of you can help me.

Santosh Reddy
19th May 2011, 17:45
So here what happens, as your QThread::run is conituous loop, it would never stop, as if it were the only thread running on the system, will always try to occupy the CPU, you should notice that CPU utilization goes >90% or even 100%. the best alternate is to have a defined cylce time / frequency at which this loop should execute.

Lets see, as you said 200ms is the thread running time, it is better to have somthing >250ms as your cycle time, you can do this with simple QTimer, with QObject::timerEvent(), you may not need a QThread.

If you have to use QThread, have a QTimer in the thread and then use its timerEvent() to performLongTask()

Using a while(true) in QThread::run() is infact very bad idea. It will work, but it will the only think which will work on the system, every thing else will suffer.

justoit
19th May 2011, 18:05
Thank you.

I have a multicore machine so I expected Windows to move the worker thread to another core to ensure GUI responsibility. Is there a manual way to achieve this? I already tried a timer with a delay of >200 ms but it resulted in a choppy behaviour where the 200ms that longTask needs to execute caused the GUI to stall for that time. As for the question concerning QThread: I dont'really need threads; what I need is a way of performing this constantly running computation (before you ask: I cannot split that task into smaller parts) in the background while providing the user with a smooth GUI.

Santosh Reddy
19th May 2011, 18:12
Ok, try adding QThread::msleep(10), in the while(true) loop, just to check the GUI response, its not very good programming, but may be used as short term workaround. (it's an old sleep trick)

and about configuring a thread to run on a dedicated core, it is surely possible (may be at processes level), I am not aware of any Qt support for multi core, but native calls do exsist which I am not aware of again.

justoit
19th May 2011, 18:30
The msleep greatly increased GUI responsibility, but it's still far from being smooth. This may work for my development but I cant have a finished product be slow like this - especially since there re more time consuming features that I still have to add. But thank you, even this limited GUI response is better than what I had before.
As for moving threads manually: This seems to require work with the win32 api, so I would have to check for the win32 implementation of the QThread class to get the thread's system handle. From my experience, mixing Qt and winapi on that low level usually means lots of trouble. Also most sources advice not to interfere with the OS if it comes to thread-core assignment. I have enough cores so I could even have one exclusively for my worker thread but I would like to stick with "good coding" practices before I start hacking around. I still believe that there is a Qt-compliant solution for my problem and that I am just using the QThread class in a wrong way.

squidge
19th May 2011, 18:35
You might be using QThread the wrong way, post the code that is managing this thread.

If you thread really needs 100% of the CPU whilst running you might want to change the priority of the thread so other threads in the system do not suffer.

justoit
19th May 2011, 18:44
As for now, I have no special managing yet. I start the thread right in the constructor of my base widget:

myThreadInstance = new MyThread();
myThreadInstance ->start();

//edit:
This actual thread is not even blocking the CPU, it's a manager for some OpenCL code that is basically making some stuff on the GPU. So most of the time it's just waiting for the GPU to finish it's work. I am currently working on making the GPU computation non-blocking so that the thread and the machine are not stalled, however I've had this issue with other threads that work heavily on the CPU so I would like to have a general approach towards the problem of threads that use a lot of CPU time.

Lesiok
19th May 2011, 20:40
Try to do this something like :

void myThread::run()
{
initSomeStuff();
QTimer::singleShot(0,this,SLOT(oneLongTask());
exec();//start event loop
}
void myThread::oneLongTask()
{
performLongTask();
QTimer::singleShot(0,this,SLOT(oneLongTask());
}
QTimer events are serviced after all another events.

yeye_olive
20th May 2011, 15:22
@Lesiok

There is a problem with your code. oneLongTask() being a slot of the myThread class, it will be run in the context of the thread in which the myThread instance lives (presumably the GUI thread), instead of the thread it manages.

One possible solution is to move the slot to a custom class and to create an instance on the stack in the new thread:


void myThread::run()
{
initSomeStuff();
MyWorker worker;
QTimer::singleShot(0,&worker,SLOT(oneLongTask());
exec();//start event loop
}
void MyWorker::oneLongTask()
{
performLongTask();
QTimer::singleShot(0,this,SLOT(oneLongTask());
}


One may also use a plain QThread and use QObject::moveToThread() to have a MyWorker pushed to it.

As for the problem of the OP, it is difficult to state on the matter without seeing the actual code. Also, why use a thread in the first place and not a higher-level function like QtConcurrent::run()?

Farris
20th May 2011, 15:44
As the previous poster pointed out, it is easier and cleaner to just create a worker-object that communicates only through signals and slots and move that to a QThread instance.

You can ask it to start working either by connecting it to a "start working signal" that is emitted in the main thread or by invoking it indirectly from the main thread by using the static QMetaobject::invokeMethod() call.

Something like:



worker = new WorkerClass();
connect(worker,SIGNAL(response(QString)),this,SLOT (response(QString)));
QThread *t = new QThread();
t->start(QThread::IdlePriority);
worker->moveToThread(t);
QMetaObject::invokeMethod(worker,"startWorking",Qt::QueuedConnection);

Lesiok
20th May 2011, 17:56
@Lesiok

There is a problem with your code. oneLongTask() being a slot of the myThread class, it will be run in the context of the thread in which the myThread instance lives (presumably the GUI thread), instead of the thread it manages.
I want to show only an idea of solution.