|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the documentation of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 /*! |
|
43 \example threads/waitconditions |
|
44 \title Wait Conditions Example |
|
45 |
|
46 The Wait Conditions example shows how to use QWaitCondition and |
|
47 QMutex to control access to a circular buffer shared by a |
|
48 producer thread and a consumer thread. |
|
49 |
|
50 The producer writes data to the buffer until it reaches the end |
|
51 of the buffer, at which point it restarts from the beginning, |
|
52 overwriting existing data. The consumer thread reads the data as |
|
53 it is produced and writes it to standard error. |
|
54 |
|
55 Wait conditions make it possible to have a higher level of |
|
56 concurrency than what is possible with mutexes alone. If accesses |
|
57 to the buffer were simply guarded by a QMutex, the consumer |
|
58 thread couldn't access the buffer at the same time as the |
|
59 producer thread. Yet, there is no harm in having both threads |
|
60 working on \e{different parts} of the buffer at the same time. |
|
61 |
|
62 The example comprises two classes: \c Producer and \c Consumer. |
|
63 Both inherit from QThread. The circular buffer used for |
|
64 communicating between these two classes and the synchronization |
|
65 tools that protect it are global variables. |
|
66 |
|
67 An alternative to using QWaitCondition and QMutex to solve the |
|
68 producer-consumer problem is to use QSemaphore. This is what the |
|
69 \l{threads/semaphores}{Semaphores} example does. |
|
70 |
|
71 \section1 Global Variables |
|
72 |
|
73 Let's start by reviewing the circular buffer and the associated |
|
74 synchronization tools: |
|
75 |
|
76 \snippet examples/threads/waitconditions/waitconditions.cpp 0 |
|
77 |
|
78 \c DataSize is the amount of data that the producer will generate. |
|
79 To keep the example as simple as possible, we make it a constant. |
|
80 \c BufferSize is the size of the circular buffer. It is less than |
|
81 \c DataSize, meaning that at some point the producer will reach |
|
82 the end of the buffer and restart from the beginning. |
|
83 |
|
84 To synchronize the producer and the consumer, we need two wait |
|
85 conditions and one mutex. The \c bufferNotEmpty condition is |
|
86 signalled when the producer has generated some data, telling the |
|
87 consumer that it can start reading it. The \c bufferNotFull |
|
88 condition is signalled when the consumer has read some data, |
|
89 telling the producer that it can generate more. The \c numUsedBytes |
|
90 is the number of bytes in the buffer that contain data. |
|
91 |
|
92 Together, the wait conditions, the mutex, and the \c numUsedBytes |
|
93 counter ensure that the producer is never more than \c BufferSize |
|
94 bytes ahead of the consumer, and that the consumer never reads |
|
95 data that the consumer hasn't generated yet. |
|
96 |
|
97 \section1 Producer Class |
|
98 |
|
99 Let's review the code for the \c Producer class: |
|
100 |
|
101 \snippet examples/threads/waitconditions/waitconditions.cpp 1 |
|
102 \snippet examples/threads/waitconditions/waitconditions.cpp 2 |
|
103 |
|
104 The producer generates \c DataSize bytes of data. Before it |
|
105 writes a byte to the circular buffer, it must first check whether |
|
106 the buffer is full (i.e., \c numUsedBytes equals \c BufferSize). |
|
107 If the buffer is full, the thread waits on the \c bufferNotFull |
|
108 condition. |
|
109 |
|
110 At the end, the producer increments \c numUsedBytes and signalls |
|
111 that the condition \c bufferNotEmpty is true, since \c |
|
112 numUsedBytes is necessarily greater than 0. |
|
113 |
|
114 We guard all accesses to the \c numUsedBytes variable with a |
|
115 mutex. In addition, the QWaitCondition::wait() function accepts a |
|
116 mutex as its argument. This mutex is unlocked before the thread |
|
117 is put to sleep and locked when the thread wakes up. Furthermore, |
|
118 the transition from the locked state to the wait state is atomic, |
|
119 to prevent race conditions from occurring. |
|
120 |
|
121 \section1 Consumer Class |
|
122 |
|
123 Let's turn to the \c Consumer class: |
|
124 |
|
125 \snippet examples/threads/waitconditions/waitconditions.cpp 3 |
|
126 \snippet examples/threads/waitconditions/waitconditions.cpp 4 |
|
127 |
|
128 The code is very similar to the producer. Before we read the |
|
129 byte, we check whether the buffer is empty (\c numUsedBytes is 0) |
|
130 instead of whether it's full and wait on the \c bufferNotEmpty |
|
131 condition if it's empty. After we've read the byte, we decrement |
|
132 \c numUsedBytes (instead of incrementing it), and we signal the |
|
133 \c bufferNotFull condition (instead of the \c bufferNotEmpty |
|
134 condition). |
|
135 |
|
136 \section1 The main() Function |
|
137 |
|
138 In \c main(), we create the two threads and call QThread::wait() |
|
139 to ensure that both threads get time to finish before we exit: |
|
140 |
|
141 \snippet examples/threads/waitconditions/waitconditions.cpp 5 |
|
142 \snippet examples/threads/waitconditions/waitconditions.cpp 6 |
|
143 |
|
144 So what happens when we run the program? Initially, the producer |
|
145 thread is the only one that can do anything; the consumer is |
|
146 blocked waiting for the \c bufferNotEmpty condition to be |
|
147 signalled (\c numUsedBytes is 0). Once the producer has put one |
|
148 byte in the buffer, \c numUsedBytes is \c BufferSize - 1 and the |
|
149 \c bufferNotEmpty condition is signalled. At that point, two |
|
150 things can happen: Either the consumer thread takes over and |
|
151 reads that byte, or the consumer gets to produce a second byte. |
|
152 |
|
153 The producer-consumer model presented in this example makes it |
|
154 possible to write highly concurrent multithreaded applications. |
|
155 On a multiprocessor machine, the program is potentially up to |
|
156 twice as fast as the equivalent mutex-based program, since the |
|
157 two threads can be active at the same time on different parts of |
|
158 the buffer. |
|
159 |
|
160 Be aware though that these benefits aren't always realized. |
|
161 Locking and unlocking a QMutex has a cost. In practice, it would |
|
162 probably be worthwhile to divide the buffer into chunks and to |
|
163 operate on chunks instead of individual bytes. The buffer size is |
|
164 also a parameter that must be selected carefully, based on |
|
165 experimentation. |
|
166 */ |