PDA

View Full Version : Problem with threads



kvnlinux
19th August 2009, 19:53
Hi,

i wrote a simple program, that queries the COM port. GUI specifies the data, that is sent to port, and displays the data it receives. so, my code looks like this:


/* main.cpp */
#include <QtGui/QApplication>
#include "mainwindow.h"
#include "Thread.h"

int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow window;
Thread thread;
QObject::connect(&thread, SIGNAL(update(int)), &window, SLOT(update(int)));
thread.start();
window.show();
int status = app.exec();
thread.stop();
thread.wait(1000);
return status;
}



/* Thread.h */
#include <QThread>
#include <QTimer>

class Thread : public QThread
{
Q_OBJECT
public:
Thread();
void run();
signals:
void update(int);
public slots:
void stop();
void doAction();
private:
QTimer timer;
};



/* Thread.cpp */
#include "Thread.h"

Thread::Thread() {
timer.setInterval(1000);
connect(&timer, SIGNAL(timeout()), this, SLOT(doAction()));
connect(this, SIGNAL(started()), &timer, SLOT(start()));
}

void Thread::run() {
exec();
}

void Thread::stop() {
timer.stop();
this->quit();
}

void Thread::doAction() {
here i write some data to port and then get some data
emit update(some data);
}


MainWindow is a simple window, that has a slot update.
So, the problem is that, when port is unavailable or doesn't return data, thread is trying to obtain data for several times. This operation lasts for a second and my GUI freezes for this time. I've read a nuber of books and it seems to me that my code doesn't differ from that given in the examples. Also, i haven't got this problem when i don't use event loop in my thread that is when i use msleep and while statement in QThread::run() function.

So, could anyone explain me what's wrong with my code and how could i solve this problem.
Thanks in advance.

AcerExtensa
20th August 2009, 07:12
void Thread::run() {
exec();
}
You do nothing in your thread. Only run() function is real thread and runs in another stream. all other functions in class is just for more accurate OOP. So your Thread::doAction() runs in the same thread as GUI, that is why you have freezes.

franz
20th August 2009, 07:16
This means that you have to define a QObject inside the run() function; one that actually lives in another thread. Then you can use the timer to tell that QObject to doAction().

wagmare
20th August 2009, 07:28
void Thread::run() {
exec();
}


u can do all doAction() work inside run() itself ...

void ::run()
{
while(stopped){
here i write some data to port and then get some data
emit update(some data);
sleep(1);
}
}

void Thread::stop() {
stopped = true;
wait();
}


and no need to call QThread::exec();(protected) seperately as QApp->exec(); is enough

kvnlinux
20th August 2009, 10:37
This means that you have to define a QObject inside the run() function; one that actually lives in another thread. Then you can use the timer to tell that QObject to doAction().

Ok, i've rewrite it, so at the moment it looks like this


/* Thread.h */
#include <QThread>

class Object : public QObject
{
Q_OBJECT
public:
Object();
protected:
void timerEvent(QTimerEvent*);
signals:
void update(int);
private:
int timerId;
}

class Thread : public QThread
{
Q_OBJECT
public:
Thread();
void run();
private:
Object* object;
};



/* Thread.cpp */
#include "Thread.h"

Thread::Thread() {
}

void Thread::run() {
object = new Object();
exec();
}

Object::Object() {
timerId = startTimer(1000);
}

Object::~Object() {
killTimer(timerId);
}

void Object::timerEvent(QTimerEvent* event) {
here i write some data to port and then get some data
emit update(some data);
}

As i understood, you advised me to create an object inside QThread::run() function and then to use a timer for this object. Unfortunately, GUI is steel freezing when this object is trying to read from port. Maybe, i've missed something?

numbat
20th August 2009, 13:29
Your code works fine for me (with a sleep call in timerEvent). The update slot will run in the GUI thread, are you doing anything that could cause a freeze in that function?

kvnlinux
20th August 2009, 15:37
Your code works fine for me (with a sleep call in timerEvent). The update slot will run in the GUI thread, are you doing anything that could cause a freeze in that function?

No, inside timerEvent function i just write data to COM port and then read from port. The only thing that is slow enough is reading data from port, when there is no data to read. In this case i do up to 10 attempts to read and then return either a piece of data or error. But this function is performed within the separate thread, so it shouldn't influence the GUI thread. Update slot in GUI displays the value from timerEvent function in QLCDNumber.

Anyway, i'll rewrite my code step by step to find what causes GUI to freeze.
Thank you all, i really appreciate your help, since this is my first experience of working with threads.

PaceyIV
21st August 2009, 17:29
Why you check the serial port with a timer instead of doing it directly in the run() of the thread?

I use QextSerialPort and I check the available data in a thread like this:



class ReceiveThread : public QThread
{
Q_OBJECT

public:
ReceiveThread(QextSerialPort *adrPort);
~ReceiveThread();

void stopReceiving();

protected:
void run();

signals:
void dataReceived(const QByteArray); //!< Data Available from the QextSerialPort

private:
QextSerialPort *d_port; //!< Reference to the serial port to monitor
QMutex mutex; //!< Mutex lock
bool stopped; //!< Specify if the thread is running or not (it is needed to stop the thread)
};

ReceiveThread::ReceiveThread(QextSerialPort *adrPort)
: d_port(adrPort), stopped(false)
{
}

ReceiveThread::~ReceiveThread()
{
if (isRunning())
{
stopReceiving();
wait();
}
}

void ReceiveThread::stopReceiving()
{
stopped = true;
}

//! The Receive Thread Loop
void ReceiveThread::run()
{
QByteArray data;
int bytesAvailable;
data.reserve(MAX_BUFFER_SIZE);

while(!stopped)
{
mutex.lock();
bytesAvailable = d_port->bytesAvailable();
if (bytesAvailable > 0)
data.append(d_port->read((bytesAvailable<MAX_BUFFER_SIZE)? bytesAvailable: MAX_BUFFER_SIZE));
mutex.unlock();

if (bytesAvailable)
qDebug() << tr("ReceiveThread: %1").arg(bytesAvailable);

if (!data.isEmpty())
{
emit dataReceived(data);
qDebug() << tr("ReceiveThread: Emitted Data Received");
qDebug() << tr("ReceiveThread: Data: 0x%1").arg((QString)data.toHex());
data.clear();
}

msleep(RECEIVE_THREAD_SLEEP_TIME);
}
}


To use this class you only nead to istantiete the class and call the run() method to start the monitoring of the serial port.
To stop the monitoring you must call the stopReceiving() method.

The data received is emitted with the signal dataReceived(const QByteArray &), so you have to connect this with a slot to manage the data received.

WARNING
This class does not perform any check on the QextSerialPort provided, you have to perform these check before starting the Receive Thread. Also you have to stop the Receive Thread before close the QextSerialPort.

So, instead to instantiate the Thread directly in the Main you have to instantiate it in your MainWindow after create and open the serialport.