diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
commit | f15b8a83e2e51955776a3f07cb85ebfc342dd8ef (patch) | |
tree | c5dc684986051654898db11ce73e03b9fec8db99 /doc/src/examples/waitconditions.qdoc | |
download | Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.zip Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.gz Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.bz2 |
Initial import of statemachine branch from the old kinetic repository
Diffstat (limited to 'doc/src/examples/waitconditions.qdoc')
-rw-r--r-- | doc/src/examples/waitconditions.qdoc | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/doc/src/examples/waitconditions.qdoc b/doc/src/examples/waitconditions.qdoc new file mode 100644 index 0000000..dce0411 --- /dev/null +++ b/doc/src/examples/waitconditions.qdoc @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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. +*/ |