PDA

View Full Version : Two threads in a console application



msr
26th September 2011, 02:00
Hello,

Im developing a console application in which the main thread is continuously reading and parsing user input commands. One of that commands is called "loop" and with that I want to start loop after the user press ENTER and stops it when the user press ENTER again.

So I have something like this:

(MainThread)


while(getchar() != '\n') QCoreApplication::processEvents();
loop->start();
while(getchar() != '\n') QCoreApplication::processEvents();
loop->stop();

(ReadLoopThread)

while(condition)
{
// read data from a serial device
// print data on console
// sleep 'x' miliseconds
}

What's the correct way to implement this?

I tried many solutions (none worked well):
1) create a thread with a timer that emits a signal every 'x' miliseconds (avoiding the use of sleep()) and connected to a slot that reads data from serial device and print data in console if a global boolean variable is 'true' (meaning Im on the "loop" command); not efficient - timer is always emitting the signal
2) two mutexes to control/block the loop as needed; it works but if I call QThread::sleep() it the thread blocks (why?)

Now Im thinking on using .moveToThread() of QObject class but I really need to control the periodicity of loop and that way I guess I can't use QThread::sleep().

The code in main():

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

Commander *commander = new Commander();
QObject::connect(commander, SIGNAL(end()), &a, SLOT(quit()));
QTimer::singleShot(0, commander, SLOT(start())); // start the thread
QTimer::singleShot(10, commander, SLOT(console())); // start console's loop

return a.exec();
}

Commander inherits QThread. console() is the where I get (getline()) and parse user input.

The ReadLoopThread:

class ReadLoop : public QThread
{
Q_OBJECT
public:
ReadLoop(Commander *commander) {m_commander = commander;}
public slots:
void run();
private:
Commander *m_commander;
};

Control implemented with mutexes:

void ReadLoop::run()
{
forever
{
qDebug() << "readLoop is waiting to start";
start_mutex.lock();
start_mutex.unlock();
while(!stop_mutex.tryLock())
{
qDebug() << "readLoop'ing";
//QThread::sleep(1000); // DOESN'T WORK! Block the thread.
// Without it, qDebug() executes "as fast as possible" - not acceptable
}
stop_mutex.unlock();
}
}


Any help is GREATLY appreciated.

high_flyer
28th September 2011, 16:23
Let start with:


while(getchar() != '\n') QCoreApplication::processEvents();
loop->start();

loop->start() will never get called since you didn't use scope for the while().

amleto
28th September 2011, 20:03
Let start with:


while(getchar() != '\n') QCoreApplication::processEvents();
loop->start();

loop->start() will never get called since you didn't use scope for the while().

start will be called when the condition is false.

high_flyer
30th September 2011, 17:16
start will be called when the condition is false.
Ah right - thats what happens when I read posts between compiles...:rolleyes:


1) create a thread with a timer that emits a signal every 'x' miliseconds (avoiding the use of sleep()) and connected to a slot that reads data from serial device and print data in console if a global boolean variable is 'true' (meaning Im on the "loop" command); not efficient - timer is always emitting the signal
Well, not sure about "not efficient".
Since you are not working with interrupts, you can't avoid polling.
Running a timer might even be more efficient then a thread - as timer is triggered by a HW interrupt, while a thread is running all the time...
If what ever milliseconds are good enough for you (and since you are reading from a serial port you probably wont need anything faster than 10ms per polling) this is the easier, and simpler solution, and probably more efficient too, which I would tend to do.


2) two mutexes to control/block the loop as needed; it works but if I call QThread::sleep() it the thread blocks (why?)
In your code I don't see where you defined and allocated your mutexes.
Probably you have a thread affinity problem, so that you lock the same mutex twice before you unlock it.
Also, you are misusing mutexes to what they are not intended.
Which explains the code that makes little sense if you use mutexes as you should:


start_mutex.lock();
start_mutex.unlock();

Mutex is for protecting mutual exclusive data, not for conditional running.
There are other mechanisms for that, such as semaphores or wait conditions.
Just locking and immediately unlocking a mutex makes no sense if you are not protecting anything between the lock()/unlock(), and that code should be the same if you just delete both lines.
I know what you wanted to achieve but that is the wrong way to go about it.
As you are clearly not quite proficient with threading concepts, stick to the timer solution.
It is a good solution, and a simple one.