PDA

View Full Version : Producer Consumer



^NyAw^
16th November 2006, 21:27
Hi,
I'm trying to develop an application that has 2 Threads. One Thread is a Producer and the other one is a Consumer. The two Threads have acces to two semaphores that lock them like Producer-Consumer algorithm. The Producer insert data into an array and the Consumer reads the data.

Producer code:


Producer::run()
{
while (bRun)
{
m_qUsedData->acquire();
//put data into an array
m_iIndex = ((m_iIndex+1)%MAX); //Jump to the next position of the array
m_qFreeData->release();
}
}




Consumer::run()
{
while (bRun)
{
m_qFreeData->acquire();
//read data from the array and do some work
m_iIndex = ((m_iIndex+1)%MAX); //Jump to the next position of the array
m_qUsedData->release();
}
}


When I stop the program, first I stop the Producer Thread, then the Consumer putting their "bRun" variables to false.
The problem is that if the Consumer is locked into "m_qFreeData->acquire();" they will never exit from "run()" so it will never be really stopped.

The program let's the user to start and stop the threads. When it stopped, other internal changes are made: the Consumer Thread reads images from a Camera and the Camera that we are acquiring images could be changed, so I have to reinitilize the Thread but the main program is stopped into ConsumerThread->wait();

Is there anyway to unlock the Consumer Thread?
I have also tryied to use "tryAcquire()" but it didn't work.

Thanks,

jacek
16th November 2006, 22:03
Try QWaitCondition and QMutex instead of semaphores. This way you'll be able to wake up the consumer, but remember that it has to check the value of bRun after it wakes up.

wysota
16th November 2006, 22:20
If you remember to release the lock before exiting producer's run(), the consumer should be able to continue, just be sure to set some flag in the producer so that the consumer exits instead of trying to aquire the lock again. This way it should also work with semaphores.

Of course it is easier to use a wait condition like Jacek suggested, but you'll still need to flag the consumer so that it exits before aquiring the lock again. Using wait conditions won't really change much here (I guess it would be more usefull if you had more consumers), but it'll look nicer ;)

In both cases you have to set the flag before the producer releases the mutex/semaphore! Otherwise the consumer might jump into the critical section once (or more times) too many.

^NyAw^
17th November 2006, 12:48
Hi,
Ok, I have changed the Semaphores with two WaitConditions and a Mutex.

Now it's working if when I stop the program don't call "wait()" of the two Threads.

When the Producer stops (exit from "while(bRun)") I force a wakeAll to let the Consumer wake if it is waiting. When it wakes it looks the "bRun" variable and if it is true it does it's jobs and if it is false it doesn't do anything and in the "while(bRun)" it will exit. Thanks for this jacek.

I don't understand why if I call wait() the Consumer hangs. I'm sure that the Producer calls wakeAll. Now it works and really does'nt matter this little problem, but it will be nice to know why it hangs.

Thanks jacek, and wysota.

jacek
17th November 2006, 12:59
I don't understand why if I call wait() the Consumer hangs.
May we see the code?

^NyAw^
17th November 2006, 13:05
Wow !!!
Not working!

Producer Code:


while (m_bRun)
{
m_pqThreadMutex->lock();
if (*m_piUsedBytes == MAX_IMATGES)
{
m_pqBufferNotFull->wait(m_pqThreadMutex);
}
m_pqThreadMutex->unlock();
//Put data
m_iIndex = ((m_iIndex+1)%MAX);

m_pqThreadMutex->lock();
(*m_piUsedBytes )++;
m_pqBufferNotEmpty->wakeAll();
m_pqThreadMutex->unlock();
}
m_pqBufferNotEmpty->wakeAll(); //Wake up the Consumer



Consumer Code:


while (m_bRun)
{
m_pqThreadMutex->lock();
if (*m_piUsedBytes == 0)
{
m_pqBufferNotEmpty->wait(m_pqThreadMutex);
}
m_pqThreadMutex->unlock();
if (bRun) //Check if has to be stopped
{
//Get data
m_iIndex = ((m_iIndex+1)%MAX);

m_pqThreadMutex->lock();
(*m_piUsedBytes )--;
m_pqBufferNotFull->wakeAll();
m_pqThreadMutex->unlock();
}
}


For the Producer and Consumer Threads I call "stop()" that changes "bRun" to false. Then I call wait().



void stop_Threads()
{
Producer->stop();
Producer->wait();

Consumer->stop();
Consumer->wait(); //Sometimes it hangs here
}

jacek
17th November 2006, 13:38
Producer->stop();
Producer->wait();
Consumer->stop();
Consumer->wait(); //Sometimes it hangs here
First you tell the producer to stop, producer wakes the consumer and then you tell the consumer to stop. Now suppose that consumer is waiting to lock the mutex on line 3 and the buffer is empty. If you stop the producer, it might happen that the consumer will acquire the lock and will hang on wait condition before the main thread invokes stop(). Similar situation might happen with the producer too, so:
1. you have to check bRun before invoking wait(),
2. you should acquire the lock before invoking stop() to make sure that both threads are outside the critical section (as they might be inside the if statement waiting for processor time to execute wait()).

jacek
17th November 2006, 14:00
2. you should acquire the lock before invoking stop() to make sure that both threads are outside the critical section (as they might be inside the if statement waiting for processor time to execute wait()).
Of course I meant the QWaitCondition::wait().

You should invoke QThread::wait() after invoking both stop() methods.

^NyAw^
17th November 2006, 15:00
mmm,
I don't understand you or it doesn't work.

Could you please modify the code I posted?

Thanks,

^NyAw^
17th November 2006, 17:25
Anyone know how to create this Producer-Consumer algorithm with infinite loop that let the user stop the Threads and start any times that the user want?

I have always a Deadlock and I'm not able to see how to avoid it.

Thanks,

jacek
17th November 2006, 17:32
Try this:

m_pqThreadMutex->lock();
Producer->stop();
Consumer->stop();
m_pqBufferNotFull->wakeAll();
m_pqBufferNotEmpty->wakeAll();
m_pqThreadMutex->unlock();

Producer->wait();
Consumer->wait();


while (m_bRun)
{
...
if (*m_piUsedBytes == 0 && bRun ) {
...
}
...
}Don't forget to add a test for bRun in Producer too.

^NyAw^
17th November 2006, 18:00
Ok,
I founded where is the error but I understand why.
In the Consumer, there is a lot of code that takes an image from the buffer and does some image processing.

If I comment all the Processing code, the Threads work correctly but uncommenting this part of the code, the Thread hangs. The Processing code is not a Critical Section and don't have any Mutex or WaitConditions.:eek: :confused: :crying:

Thanks for your help

jacek
17th November 2006, 18:07
Anyone know how to create this Producer-Consumer algorithm with infinite loop that let the user stop the Threads and start any times that the user want?
You just need another wait condition and mutex to make your threads wait for bRun to become true.

Beware that your implementation won't work if you have more that one producer or consumer.

jacek
17th November 2006, 18:08
If I comment all the Processing code, the Threads work correctly but uncommenting this part of the code, the Thread hangs.
Do you access any shared data or GUI there?

^NyAw^
17th November 2006, 18:41
Hi,


Do you access any shared data or GUI there?


I access to the buffer of images to process it, then every tool (image processing tool) process the corresponding image and finally it paints to a Widget. The Widget is contains a Window (mmm... the processing library has a window object that I used to create a Widget) creating the library window into the widget and telling it that the Widget is its father.

If I process the image but don't display it, I'm able to stop and start the program as I want to, but if I display the images into the Widget and try to stop the program it hangs.

The code of the Widget is executed by the Consumer Thread. It Process the image, take the result image and display it.

The Widget is in the Main Window.

I will investigate more, and thanks for thinking on it

jacek
17th November 2006, 20:01
The code of the Widget is executed by the Consumer Thread. It Process the image, take the result image and display it.
And that's the problem. Only the main thread (a.k.a. the GUI thread) can touch the GUI. Better send those QImages through a queued connection to the GUI thread and let it handle them.

Also read this carefully: http://doc.trolltech.com/4.2/threads.html

^NyAw^
17th November 2006, 20:53
Hey,
I will take a look at it.

Thank you very much,:)