/****************************************************************************+ −
**+ −
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the documentation of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, this file may be used under the terms of the GNU Lesser+ −
** General Public License version 2.1 as published by the Free Software+ −
** Foundation and appearing in the file LICENSE.LGPL included in the+ −
** packaging of this file. Please review the following information to+ −
** ensure the GNU Lesser General Public License version 2.1 requirements+ −
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.+ −
**+ −
** In addition, as a special exception, Nokia gives you certain additional+ −
** rights. These rights are described in the Nokia Qt LGPL Exception+ −
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
+ −
/*!+ −
\example threads/waitconditions+ −
\title Wait Conditions Example+ −
+ −
The Wait Conditions example shows how to use QWaitCondition and+ −
QMutex to control access to a circular buffer shared by a+ −
producer thread and a consumer thread.+ −
+ −
The producer writes data to the buffer until it reaches the end+ −
of the buffer, at which point it restarts from the beginning,+ −
overwriting existing data. The consumer thread reads the data as+ −
it is produced and writes it to standard error.+ −
+ −
Wait conditions make it possible to have a higher level of+ −
concurrency than what is possible with mutexes alone. If accesses+ −
to the buffer were simply guarded by a QMutex, the consumer+ −
thread couldn't access the buffer at the same time as the+ −
producer thread. Yet, there is no harm in having both threads+ −
working on \e{different parts} of the buffer at the same time.+ −
+ −
The example comprises two classes: \c Producer and \c Consumer.+ −
Both inherit from QThread. The circular buffer used for+ −
communicating between these two classes and the synchronization+ −
tools that protect it are global variables.+ −
+ −
An alternative to using QWaitCondition and QMutex to solve the+ −
producer-consumer problem is to use QSemaphore. This is what the+ −
\l{threads/semaphores}{Semaphores} example does.+ −
+ −
\section1 Global Variables+ −
+ −
Let's start by reviewing the circular buffer and the associated+ −
synchronization tools:+ −
+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 0+ −
+ −
\c DataSize is the amount of data that the producer will generate.+ −
To keep the example as simple as possible, we make it a constant.+ −
\c BufferSize is the size of the circular buffer. It is less than+ −
\c DataSize, meaning that at some point the producer will reach+ −
the end of the buffer and restart from the beginning.+ −
+ −
To synchronize the producer and the consumer, we need two wait+ −
conditions and one mutex. The \c bufferNotEmpty condition is+ −
signalled when the producer has generated some data, telling the+ −
consumer that it can start reading it. The \c bufferNotFull+ −
condition is signalled when the consumer has read some data,+ −
telling the producer that it can generate more. The \c numUsedBytes+ −
is the number of bytes in the buffer that contain data.+ −
+ −
Together, the wait conditions, the mutex, and the \c numUsedBytes+ −
counter ensure that the producer is never more than \c BufferSize+ −
bytes ahead of the consumer, and that the consumer never reads+ −
data that the consumer hasn't generated yet.+ −
+ −
\section1 Producer Class+ −
+ −
Let's review the code for the \c Producer class:+ −
+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 1+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 2+ −
+ −
The producer generates \c DataSize bytes of data. Before it+ −
writes a byte to the circular buffer, it must first check whether+ −
the buffer is full (i.e., \c numUsedBytes equals \c BufferSize).+ −
If the buffer is full, the thread waits on the \c bufferNotFull+ −
condition.+ −
+ −
At the end, the producer increments \c numUsedBytes and signalls+ −
that the condition \c bufferNotEmpty is true, since \c+ −
numUsedBytes is necessarily greater than 0.+ −
+ −
We guard all accesses to the \c numUsedBytes variable with a+ −
mutex. In addition, the QWaitCondition::wait() function accepts a+ −
mutex as its argument. This mutex is unlocked before the thread+ −
is put to sleep and locked when the thread wakes up. Furthermore,+ −
the transition from the locked state to the wait state is atomic,+ −
to prevent race conditions from occurring.+ −
+ −
\section1 Consumer Class+ −
+ −
Let's turn to the \c Consumer class:+ −
+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 3+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 4+ −
+ −
The code is very similar to the producer. Before we read the+ −
byte, we check whether the buffer is empty (\c numUsedBytes is 0)+ −
instead of whether it's full and wait on the \c bufferNotEmpty+ −
condition if it's empty. After we've read the byte, we decrement+ −
\c numUsedBytes (instead of incrementing it), and we signal the+ −
\c bufferNotFull condition (instead of the \c bufferNotEmpty+ −
condition).+ −
+ −
\section1 The main() Function+ −
+ −
In \c main(), we create the two threads and call QThread::wait()+ −
to ensure that both threads get time to finish before we exit:+ −
+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 5+ −
\snippet examples/threads/waitconditions/waitconditions.cpp 6+ −
+ −
So what happens when we run the program? Initially, the producer+ −
thread is the only one that can do anything; the consumer is+ −
blocked waiting for the \c bufferNotEmpty condition to be+ −
signalled (\c numUsedBytes is 0). Once the producer has put one+ −
byte in the buffer, \c numUsedBytes is \c BufferSize - 1 and the+ −
\c bufferNotEmpty condition is signalled. At that point, two+ −
things can happen: Either the consumer thread takes over and+ −
reads that byte, or the consumer gets to produce a second byte.+ −
+ −
The producer-consumer model presented in this example makes it+ −
possible to write highly concurrent multithreaded applications.+ −
On a multiprocessor machine, the program is potentially up to+ −
twice as fast as the equivalent mutex-based program, since the+ −
two threads can be active at the same time on different parts of+ −
the buffer.+ −
+ −
Be aware though that these benefits aren't always realized.+ −
Locking and unlocking a QMutex has a cost. In practice, it would+ −
probably be worthwhile to divide the buffer into chunks and to+ −
operate on chunks instead of individual bytes. The buffer size is+ −
also a parameter that must be selected carefully, based on+ −
experimentation.+ −
*/+ −