diff options
Diffstat (limited to 'doc/src/examples/semaphores.qdoc')
-rw-r--r-- | doc/src/examples/semaphores.qdoc | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/doc/src/examples/semaphores.qdoc b/doc/src/examples/semaphores.qdoc new file mode 100644 index 0000000..a387350 --- /dev/null +++ b/doc/src/examples/semaphores.qdoc @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** 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/semaphores + \title Semaphores Example + + The Semaphores example shows how to use QSemaphore 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. + + Semaphores make it possible to have a higher level of concurrency + than mutexes. If accesses to the buffer were 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 semaphores that + protect it are global variables. + + An alternative to using QSemaphore to solve the producer-consumer + problem is to use QWaitCondition and QMutex. This is what the + \l{threads/waitconditions}{Wait Conditions} example does. + + \section1 Global Variables + + Let's start by reviewing the circular buffer and the associated + semaphores: + + \snippet examples/threads/semaphores/semaphores.cpp 0 + + \c DataSize is the amout 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 + semaphores. The \c freeBytes semaphore controls the "free" area + of the buffer (the area that the producer hasn't filled with data + yet or that the consumer has already read). The \c usedBytes + semaphore controls the "used" area of the buffer (the area that + the producer has filled but that the consumer hasn't read yet). + + Together, the semaphores ensure that the producer is never more + than \c BufferSize bytes ahead of the consumer, and that the + consumer never reads data that the producer hasn't generated yet. + + The \c freeBytes semaphore is initialized with \c BufferSize, + because initially the entire buffer is empty. The \c usedBytes + semaphore is initialized to 0 (the default value if none is + specified). + + \section1 Producer Class + + Let's review the code for the \c Producer class: + + \snippet examples/threads/semaphores/semaphores.cpp 1 + \snippet examples/threads/semaphores/semaphores.cpp 2 + + The producer generates \c DataSize bytes of data. Before it + writes a byte to the circular buffer, it must acquire a "free" + byte using the \c freeBytes semaphore. The QSemaphore::acquire() + call might block if the consumer hasn't kept up the pace with the + producer. + + At the end, the producer releases a byte using the \c usedBytes + semaphore. The "free" byte has successfully been transformed into + a "used" byte, ready to be read by the consumer. + + \section1 Consumer Class + + Let's now turn to the \c Consumer class: + + \snippet examples/threads/semaphores/semaphores.cpp 3 + \snippet examples/threads/semaphores/semaphores.cpp 4 + + The code is very similar to the producer, except that this time + we acquire a "used" byte and release a "free" byte, instead of + the opposite. + + \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/semaphores/semaphores.cpp 5 + \snippet examples/threads/semaphores/semaphores.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 usedBytes semaphore to be released (its + initial \l{QSemaphore::available()}{available()} count is 0). + Once the producer has put one byte in the buffer, + \c{freeBytes.available()} is \c BufferSize - 1 and + \c{usedBytes.available()} is 1. 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. + Acquiring and releasing a QSemaphore 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. +*/ |