diff options
Diffstat (limited to 'examples/threads/doc/src')
-rw-r--r-- | examples/threads/doc/src/mandelbrot.qdoc | 370 | ||||
-rw-r--r-- | examples/threads/doc/src/queuedcustomtype.qdoc | 166 | ||||
-rw-r--r-- | examples/threads/doc/src/semaphores.qdoc | 147 | ||||
-rw-r--r-- | examples/threads/doc/src/waitconditions.qdoc | 154 |
4 files changed, 0 insertions, 837 deletions
diff --git a/examples/threads/doc/src/mandelbrot.qdoc b/examples/threads/doc/src/mandelbrot.qdoc deleted file mode 100644 index c1393769f1..0000000000 --- a/examples/threads/doc/src/mandelbrot.qdoc +++ /dev/null @@ -1,370 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: http://www.gnu.org/copyleft/fdl.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \example mandelbrot - \title Mandelbrot Example - \ingroup qtconcurrent-mtexamples - - \brief The Mandelbrot example demonstrates multi-thread programming - using Qt. It shows how to use a worker thread to - perform heavy computations without blocking the main thread's - event loop. - - \image mandelbrot-example.png Screenshot of the Mandelbrot example - - The heavy computation here is the Mandelbrot set, probably the - world's most famous fractal. These days, while sophisticated - programs such as \l{http://xaos.sourceforge.net/}{XaoS} that provide real-time zooming in the - Mandelbrot set, the standard Mandelbrot algorithm is just slow - enough for our purposes. - - In real life, the approach described here is applicable to a - large set of problems, including synchronous network I/O and - database access, where the user interface must remain responsive - while some heavy operation is taking place. The \l - {Blocking Fortune Client Example} shows the same principle at - work in a TCP client. - - The Mandelbrot application supports zooming and scrolling using - the mouse or the keyboard. To avoid freezing the main thread's - event loop (and, as a consequence, the application's user - interface), we put all the fractal computation in a separate - worker thread. The thread emits a signal when it is done - rendering the fractal. - - During the time where the worker thread is recomputing the - fractal to reflect the new zoom factor position, the main thread - simply scales the previously rendered pixmap to provide immediate - feedback. The result doesn't look as good as what the worker - thread eventually ends up providing, but at least it makes the - application more responsive. The sequence of screenshots below - shows the original image, the scaled image, and the rerendered - image. - - \table - \row - \li \inlineimage mandelbrot_zoom1.png - \li \inlineimage mandelbrot_zoom2.png - \li \inlineimage mandelbrot_zoom3.png - \endtable - - Similarly, when the user scrolls, the previous pixmap is scrolled - immediately, revealing unpainted areas beyond the edge of the - pixmap, while the image is rendered by the worker thread. - - \table - \row - \li \inlineimage mandelbrot_scroll1.png - \li \inlineimage mandelbrot_scroll2.png - \li \inlineimage mandelbrot_scroll3.png - \endtable - - The application consists of two classes: - - \list - \li \c RenderThread is a QThread subclass that renders - the Mandelbrot set. - \li \c MandelbrotWidget is a QWidget subclass that shows the - Mandelbrot set on screen and lets the user zoom and scroll. - \endlist - - If you are not already familiar with Qt's thread support, we - recommend that you start by reading the \l{Thread Support in Qt} - overview. - - \section1 RenderThread Class Definition - - We'll start with the definition of the \c RenderThread class: - - \snippet mandelbrot/renderthread.h 0 - - The class inherits QThread so that it gains the ability to run in - a separate thread. Apart from the constructor and destructor, \c - render() is the only public function. Whenever the thread is done - rendering an image, it emits the \c renderedImage() signal. - - The protected \c run() function is reimplemented from QThread. It - is automatically called when the thread is started. - - In the \c private section, we have a QMutex, a QWaitCondition, - and a few other data members. The mutex protects the other data - member. - - \section1 RenderThread Class Implementation - - \snippet mandelbrot/renderthread.cpp 0 - - In the constructor, we initialize the \c restart and \c abort - variables to \c false. These variables control the flow of the \c - run() function. - - We also initialize the \c colormap array, which contains a series - of RGB colors. - - \snippet mandelbrot/renderthread.cpp 1 - - The destructor can be called at any point while the thread is - active. We set \c abort to \c true to tell \c run() to stop - running as soon as possible. We also call - QWaitCondition::wakeOne() to wake up the thread if it's sleeping. - (As we will see when we review \c run(), the thread is put to - sleep when it has nothing to do.) - - The important thing to notice here is that \c run() is executed - in its own thread (the worker thread), whereas the \c - RenderThread constructor and destructor (as well as the \c - render() function) are called by the thread that created the - worker thread. Therefore, we need a mutex to protect accesses to - the \c abort and \c condition variables, which might be accessed - at any time by \c run(). - - At the end of the destructor, we call QThread::wait() to wait - until \c run() has exited before the base class destructor is - invoked. - - \snippet mandelbrot/renderthread.cpp 2 - - The \c render() function is called by the \c MandelbrotWidget - whenever it needs to generate a new image of the Mandelbrot set. - The \c centerX, \c centerY, and \c scaleFactor parameters specify - the portion of the fractal to render; \c resultSize specifies the - size of the resulting QImage. - - The function stores the parameters in member variables. If the - thread isn't already running, it starts it; otherwise, it sets \c - restart to \c true (telling \c run() to stop any unfinished - computation and start again with the new parameters) and wakes up - the thread, which might be sleeping. - - \snippet mandelbrot/renderthread.cpp 3 - - \c run() is quite a big function, so we'll break it down into - parts. - - The function body is an infinite loop which starts by storing the - rendering parameters in local variables. As usual, we protect - accesses to the member variables using the class's mutex. Storing - the member variables in local variables allows us to minimize the - amout of code that needs to be protected by a mutex. This ensures - that the main thread will never have to block for too long when - it needs to access \c{RenderThread}'s member variables (e.g., in - \c render()). - - The \c forever keyword is, like \c foreach, a Qt pseudo-keyword. - - \snippet mandelbrot/renderthread.cpp 4 - \snippet mandelbrot/renderthread.cpp 5 - \snippet mandelbrot/renderthread.cpp 6 - \snippet mandelbrot/renderthread.cpp 7 - - Then comes the core of the algorithm. Instead of trying to create - a perfect Mandelbrot set image, we do multiple passes and - generate more and more precise (and computationally expensive) - approximations of the fractal. - - If we discover inside the loop that \c restart has been set to \c - true (by \c render()), we break out of the loop immediately, so - that the control quickly returns to the very top of the outer - loop (the \c forever loop) and we fetch the new rendering - parameters. Similarly, if we discover that \c abort has been set - to \c true (by the \c RenderThread destructor), we return from - the function immediately, terminating the thread. - - The core algorithm is beyond the scope of this tutorial. - - \snippet mandelbrot/renderthread.cpp 8 - \snippet mandelbrot/renderthread.cpp 9 - - Once we're done with all the iterations, we call - QWaitCondition::wait() to put the thread to sleep by calling, - unless \c restart is \c true. There's no use in keeping a worker - thread looping indefinitely while there's nothing to do. - - \snippet mandelbrot/renderthread.cpp 10 - - The \c rgbFromWaveLength() function is a helper function that - converts a wave length to a RGB value compatible with 32-bit - \l{QImage}s. It is called from the constructor to initialize the - \c colormap array with pleasing colors. - - \section1 MandelbrotWidget Class Definition - - The \c MandelbrotWidget class uses \c RenderThread to draw the - Mandelbrot set on screen. Here's the class definition: - - \snippet mandelbrot/mandelbrotwidget.h 0 - - The widget reimplements many event handlers from QWidget. In - addition, it has an \c updatePixmap() slot that we'll connect to - the worker thread's \c renderedImage() signal to update the - display whenever we receive new data from the thread. - - Among the private variables, we have \c thread of type \c - RenderThread and \c pixmap, which contains the last rendered - image. - - \section1 MandelbrotWidget Class Implementation - - \snippet mandelbrot/mandelbrotwidget.cpp 0 - - The implementation starts with a few contants that we'll need - later on. - - \snippet mandelbrot/mandelbrotwidget.cpp 1 - - The interesting part of the constructor is the - qRegisterMetaType() and QObject::connect() calls. Let's start - with the \l{QObject::connect()}{connect()} call. - - Although it looks like a standard signal-slot connection between - two \l{QObject}s, because the signal is emitted in a different - thread than the receiver lives in, the connection is effectively a - \l{Qt::QueuedConnection}{queued connection}. These connections are - asynchronous (i.e., non-blocking), meaning that the slot will be - called at some point after the \c emit statement. What's more, the - slot will be invoked in the thread in which the receiver lives. - Here, the signal is emitted in the worker thread, and the slot is - executed in the GUI thread when control returns to the event loop. - - With queued connections, Qt must store a copy of the arguments - that were passed to the signal so that it can pass them to the - slot later on. Qt knows how to take of copy of many C++ and Qt - types, but QImage isn't one of them. We must therefore call the - template function qRegisterMetaType() before we can use QImage - as parameter in queued connections. - - \snippet mandelbrot/mandelbrotwidget.cpp 2 - \snippet mandelbrot/mandelbrotwidget.cpp 3 - \snippet mandelbrot/mandelbrotwidget.cpp 4 - - In \l{QWidget::paintEvent()}{paintEvent()}, we start by filling - the background with black. If we have nothing yet to paint (\c - pixmap is null), we print a message on the widget asking the user - to be patient and return from the function immediately. - - \snippet mandelbrot/mandelbrotwidget.cpp 5 - \snippet mandelbrot/mandelbrotwidget.cpp 6 - \snippet mandelbrot/mandelbrotwidget.cpp 7 - \snippet mandelbrot/mandelbrotwidget.cpp 8 - - If the pixmap has the right scale factor, we draw the pixmap directly onto - the widget. Otherwise, we scale and translate the \l{Coordinate - System}{coordinate system} before we draw the pixmap. By reverse mapping - the widget's rectangle using the scaled painter matrix, we also make sure - that only the exposed areas of the pixmap are drawn. The calls to - QPainter::save() and QPainter::restore() make sure that any painting - performed afterwards uses the standard coordinate system. - - \snippet mandelbrot/mandelbrotwidget.cpp 9 - - At the end of the paint event handler, we draw a text string and - a semi-transparent rectangle on top of the fractal. - - \snippet mandelbrot/mandelbrotwidget.cpp 10 - - Whenever the user resizes the widget, we call \c render() to - start generating a new image, with the same \c centerX, \c - centerY, and \c curScale parameters but with the new widget size. - - Notice that we rely on \c resizeEvent() being automatically - called by Qt when the widget is shown the first time to generate - the image the very first time. - - \snippet mandelbrot/mandelbrotwidget.cpp 11 - - The key press event handler provides a few keyboard bindings for - the benefit of users who don't have a mouse. The \c zoom() and \c - scroll() functions will be covered later. - - \snippet mandelbrot/mandelbrotwidget.cpp 12 - - The wheel event handler is reimplemented to make the mouse wheel - control the zoom level. QWheelEvent::delta() returns the angle of - the wheel mouse movement, in eights of a degree. For most mice, - one wheel step corresponds to 15 degrees. We find out how many - mouse steps we have and determine the zoom factor in consequence. - For example, if we have two wheel steps in the positive direction - (i.e., +30 degrees), the zoom factor becomes \c ZoomInFactor - to the second power, i.e. 0.8 * 0.8 = 0.64. - - \snippet mandelbrot/mandelbrotwidget.cpp 13 - - When the user presses the left mouse button, we store the mouse - pointer position in \c lastDragPos. - - \snippet mandelbrot/mandelbrotwidget.cpp 14 - - When the user moves the mouse pointer while the left mouse button - is pressed, we adjust \c pixmapOffset to paint the pixmap at a - shifted position and call QWidget::update() to force a repaint. - - \snippet mandelbrot/mandelbrotwidget.cpp 15 - - When the left mouse button is released, we update \c pixmapOffset - just like we did on a mouse move and we reset \c lastDragPos to a - default value. Then, we call \c scroll() to render a new image - for the new position. (Adjusting \c pixmapOffset isn't sufficient - because areas revealed when dragging the pixmap are drawn in - black.) - - \snippet mandelbrot/mandelbrotwidget.cpp 16 - - The \c updatePixmap() slot is invoked when the worker thread has - finished rendering an image. We start by checking whether a drag - is in effect and do nothing in that case. In the normal case, we - store the image in \c pixmap and reinitialize some of the other - members. At the end, we call QWidget::update() to refresh the - display. - - At this point, you might wonder why we use a QImage for the - parameter and a QPixmap for the data member. Why not stick to one - type? The reason is that QImage is the only class that supports - direct pixel manipulation, which we need in the worker thread. On - the other hand, before an image can be drawn on screen, it must - be converted into a pixmap. It's better to do the conversion once - and for all here, rather than in \c paintEvent(). - - \snippet mandelbrot/mandelbrotwidget.cpp 17 - - In \c zoom(), we recompute \c curScale. Then we call - QWidget::update() to draw a scaled pixmap, and we ask the worker - thread to render a new image corresponding to the new \c curScale - value. - - \snippet mandelbrot/mandelbrotwidget.cpp 18 - - \c scroll() is similar to \c zoom(), except that the affected - parameters are \c centerX and \c centerY. - - \section1 The main() Function - - The application's multithreaded nature has no impact on its \c - main() function, which is as simple as usual: - - \snippet mandelbrot/main.cpp 0 -*/ diff --git a/examples/threads/doc/src/queuedcustomtype.qdoc b/examples/threads/doc/src/queuedcustomtype.qdoc deleted file mode 100644 index 40ec2668de..0000000000 --- a/examples/threads/doc/src/queuedcustomtype.qdoc +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: http://www.gnu.org/copyleft/fdl.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \example queuedcustomtype - \title Queued Custom Type Example - \brief Demonstrates multi-thread programming using Qt - \ingroup qtconcurrent-mtexamples - - \brief The Queued Custom Type example shows how to send custom types between - threads with queued signals and slots. - - \image queuedcustomtype-example.png - - Contents: - - \tableofcontents - - \section1 Overview - - In the \l{Custom Type Example}, we showed how to integrate custom types with - the meta-object system, enabling them to be stored in QVariant objects, written - out in debugging information and used in signal-slot communication. - - In this example, we create a new value class, \c Block, and register it - with the meta-object system to enable us to send instances of it between - threads using queued signals and slots. - - \section1 The Block Class - - The \c Block class is similar to the \c Message class described in the - \l{Custom Type Example}. It provides the default constructor, copy - constructor and destructor in the public section of the class that the - meta-object system requires. It describes a colored rectangle. - - \snippet queuedcustomtype/block.h custom type definition and meta-type declaration - - We will still need to register it with the meta-object system at - run-time by calling the qRegisterMetaType() template function before - we make any signal-slot connections that use this type. - Even though we do not intend to use the type with QVariant in this example, - it is good practice to also declare the new type with Q_DECLARE_METATYPE(). - - The implementation of the \c Block class is trivial, so we avoid quoting - it here. - - \section1 The Window Class - - We define a simple \c Window class with a public slot that accepts a - \c Block object. The rest of the class is concerned with managing the - user interface and handling images. - - \snippet queuedcustomtype/window.h Window class definition - - The \c Window class also contains a worker thread, provided by a - \c RenderThread object. This will emit signals to send \c Block objects - to the window's \c addBlock(Block) slot. - - The parts of the \c Window class that are most relevant are the constructor - and the \c addBlock(Block) slot. - - The constructor creates a thread for rendering images, sets up a user - interface containing a label and two push buttons that are connected to - slots in the same class. - - \snippet queuedcustomtype/window.cpp Window constructor start - \snippet queuedcustomtype/window.cpp set up widgets and connections - \snippet queuedcustomtype/window.cpp connecting signal with custom type - - In the last of these connections, we connect a signal in the - \c RenderThread object to the \c addBlock(Block) slot in the window. - - \dots - \snippet queuedcustomtype/window.cpp Window constructor finish - - The rest of the constructor simply sets up the layout of the window. - - The \c addBlock(Block) slot receives blocks from the rendering thread via - the signal-slot connection set up in the constructor: - - \snippet queuedcustomtype/window.cpp Adding blocks to the display - - We simply paint these onto the label as they arrive. - - \section1 The RenderThread Class - - The \c RenderThread class processes an image, creating \c Block objects - and using the \c sendBlock(Block) signal to send them to other components - in the example. - - \snippet queuedcustomtype/renderthread.h RenderThread class definition - - The constructor and destructor are not quoted here. These take care of - setting up the thread's internal state and cleaning up when it is destroyed. - - Processing is started with the \c processImage() function, which calls the - \c RenderThread class's reimplementation of the QThread::run() function: - - \snippet queuedcustomtype/renderthread.cpp processing the image (start) - - Ignoring the details of the way the image is processed, we see that the - signal containing a block is emitted in the usual way: - - \dots - \snippet queuedcustomtype/renderthread.cpp processing the image (finish) - - Each signal that is emitted will be queued and delivered later to the - window's \c addBlock(Block) slot. - - \section1 Registering the Type - - In the example's \c{main()} function, we perform the registration of the - \c Block class as a custom type with the meta-object system by calling the - qRegisterMetaType() template function: - - \snippet queuedcustomtype/main.cpp main function - - This call is placed here to ensure that the type is registered before any - signal-slot connections are made that use it. - - The rest of the \c{main()} function is concerned with setting a seed for - the pseudo-random number generator, creating and showing the window, and - setting a default image. See the source code for the implementation of the - \c createImage() function. - - \section1 Further Reading - - This example showed how a custom type can be registered with the - meta-object system so that it can be used with signal-slot connections - between threads. For ordinary communication involving direct signals and - slots, it is enough to simply declare the type in the way described in the - \l{Custom Type Example}. - - In practice, both the Q_DECLARE_METATYPE() macro and the qRegisterMetaType() - template function can be used to register custom types, but - qRegisterMetaType() is only required if you need to perform signal-slot - communication or need to create and destroy objects of the custom type - at run-time. - - More information on using custom types with Qt can be found in the - \l{Creating Custom Qt Types} document. -*/ diff --git a/examples/threads/doc/src/semaphores.qdoc b/examples/threads/doc/src/semaphores.qdoc deleted file mode 100644 index e90045f824..0000000000 --- a/examples/threads/doc/src/semaphores.qdoc +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: http://www.gnu.org/copyleft/fdl.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \example semaphores - \title Semaphores Example - \brief Demonstrates multi-thread programming using Qt - \ingroup qtconcurrent-mtexamples - - \brief 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{Wait Conditions Example} does. - - \section1 Global Variables - - Let's start by reviewing the circular buffer and the associated - semaphores: - - \snippet 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 semaphores/semaphores.cpp 1 - \snippet 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 semaphores/semaphores.cpp 3 - \snippet 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 semaphores/semaphores.cpp 5 - \snippet 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. -*/ diff --git a/examples/threads/doc/src/waitconditions.qdoc b/examples/threads/doc/src/waitconditions.qdoc deleted file mode 100644 index 25c9ce88fb..0000000000 --- a/examples/threads/doc/src/waitconditions.qdoc +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: http://www.gnu.org/copyleft/fdl.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \example waitconditions - \title Wait Conditions Example - \brief Demonstrates multi-thread programming using Qt - \ingroup qtconcurrent-mtexamples - - \brief 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{Semaphores Example} does. - - \section1 Global Variables - - Let's start by reviewing the circular buffer and the associated - synchronization tools: - - \snippet 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 producer hasn't generated yet. - - \section1 Producer Class - - Let's review the code for the \c Producer class: - - \snippet waitconditions/waitconditions.cpp 1 - \snippet 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 waitconditions/waitconditions.cpp 3 - \snippet 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 waitconditions/waitconditions.cpp 5 - \snippet 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. -*/ |