1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
|
<!DOCTYPE html
PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!-- saved from url=(0014)about:internet -->
<html xmlns:MSHelp="http://www.microsoft.com/MSHelp/" lang="en-us" xml:lang="en-us"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="DC.Type" content="topic">
<meta name="DC.Title" content="GUI Thread">
<meta name="DC.subject" content="GUI Thread">
<meta name="keywords" content="GUI Thread">
<meta name="DC.Relation" scheme="URI" content="../../tbb_userguide/Design_Patterns/Design_Patterns.htm">
<meta name="DC.Relation" scheme="URI" content="Non-Preemptive_Priorities.htm#Non-Preemptive_Priorities">
<meta name="DC.Relation" scheme="URI" content="Local_Serializer.htm#Local_Serializer">
<meta name="DC.Format" content="XHTML">
<meta name="DC.Identifier" content="GUI_Thread">
<link rel="stylesheet" type="text/css" href="../../intel_css_styles.css">
<title>GUI Thread</title>
<xml>
<MSHelp:Attr Name="DocSet" Value="Intel"></MSHelp:Attr>
<MSHelp:Attr Name="Locale" Value="kbEnglish"></MSHelp:Attr>
<MSHelp:Attr Name="TopicType" Value="kbReference"></MSHelp:Attr>
</xml>
</head>
<body id="GUI_Thread">
<!-- ==============(Start:NavScript)================= -->
<script src="..\..\NavScript.js" language="JavaScript1.2" type="text/javascript"></script>
<script language="JavaScript1.2" type="text/javascript">WriteNavLink(2);</script>
<!-- ==============(End:NavScript)================= -->
<a name="GUI_Thread"><!-- --></a>
<h1 class="topictitle1">GUI Thread</h1>
<div>
<div class="section"><h2 class="sectiontitle">Problem</h2>
<p>A user interface thread must remain responsive to user requests, and
must not get bogged down in long computations.
</p>
</div>
<div class="section"><h2 class="sectiontitle">Context</h2>
<p>Graphical user interfaces often have a dedicated thread ("GUI thread")
for servicing user interactions. The thread must remain responsive to user
requests even while the application has long computations running. For example,
the user might want to press a "cancel" button to stop the long running
computation. If the GUI thread takes part in the long running computation, it
will not be able to respond to user requests.
</p>
</div>
<div class="section"><h2 class="sectiontitle">Forces</h2>
<ul type="disc">
<li>
<p>The GUI thread services an event loop.
</p>
</li>
<li>
<p>The GUI thread needs to offload work onto other threads without
waiting for the work to complete.
</p>
</li>
<li>
<p>The GUI thread must be responsive to the event loop and not become
dedicated to doing the offloaded work.
</p>
</li>
</ul>
</div>
<div class="section"><h2 class="sectiontitle">Related</h2>
<ul type="disc">
<li>Non-Preemptive Priorities
</li>
<li>Local Serializer
</li>
</ul>
</div>
<div class="section"><h2 class="sectiontitle">Solution</h2>
<p>The GUI thread offloads the work by firing off a task to do it using
method
<samp class="codeph">task::enqueue</samp>. When finished, the task posts an event
to the GUI thread to indicate that the work is done. The semantics of
<samp class="codeph">enqueue</samp> cause the task to eventually run on a worker
thread distinct from the calling thread. The method was introduced in Intel®
Threading Building Blocks (Intel® TBB) 3.0.
</p>
<p>The following figure sketches the communication paths. Items in black
are executed by the GUI thread; items in blue are executed by another thread.
</p>
<div class="fignone" id="fig5"><a name="fig5"><!-- --></a><span class="figcap">GUI Thread pattern</span>
<br><img width="400" height="150" src="Images/image007.jpg"><br>
</div>
</div>
<div class="section"><h2 class="sectiontitle">Example</h2>
<p>The example is for the Microsoft Windows* operating systems, though
similar principles apply to any GUI using an event loop idiom. For each event,
the GUI thread calls a user-defined function
<samp class="codeph">WndProc</samp>. to process an event. The key parts are shown
in
<samp class="codeph"><span style="color:blue"><strong>bold font</strong></span></samp>.
</p>
<pre><span style="color:blue"><strong>// Event posted from enqueued task when it finishes its work</strong></span><strong>.</strong>
<span style="color:blue"><strong>const UINT WM_POP_FOO = WM_USER+0;</strong></span>
<span style="color:blue"><strong>// Queue for transmitting results from enqueued task to GUI thread</strong></span><span style="color:blue"><strong>.</strong></span>
<span style="color:blue"><strong>tbb::concurrent_queue<Foo>ResultQueue;</strong></span>
<span style="color:blue"><strong>// GUI thread’s private copy of most recently computed result.</strong></span>
<span style="color:blue"><strong>Foo CurrentResult;</strong></span>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_LONGRUNNINGWORK:
<span style="color:blue"><strong>// User requested a long computation. Delegate it to another thread.</strong></span>
<span style="color:blue"><strong>LaunchLongRunningWork(hWnd);</strong></span>
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
break;
<span style="color:blue"><strong>case WM_POP_FOO:</strong></span>
<span style="color:blue"><strong>// There is another result in ResultQueue for me to grab.</strong></span>
<span style="color:blue"><strong>ResultQueue.try_pop(CurrentResult);</strong></span>
<span style="color:blue"><strong>// Update the window with the latest result.</strong></span>
<span style="color:blue"><strong>RedrawWindow</strong></span>( hWnd, NULL, NULL, RDW_ERASE|RDW_INVALIDATE );
break;
case WM_PAINT:
<span style="color:blue"><strong><em>Repaint the window using CurrentResult</em></strong></span>
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc( hWnd, msg, wParam, lParam );
}
return 0;
} </pre>
<p>The GUI thread processes long computations as follows:
</p>
<ol>
<li>
<p>The GUI thread calls
<samp class="codeph">LongRunningWork</samp>, which hands off the work to a
worker thread and immediately returns.
</p>
</li>
<li>
<p>The GUI thread continues servicing the event loop. If it has to
repaint the window, it uses the value of<samp class="codeph">CurrentResult</samp>, which
is the most recent
<samp class="codeph">Foo</samp> that it has seen.
</p>
</li>
</ol>
<p>When a worker finishes the long computation, it pushes the result into
ResultQueue, and sends a message WM_POP_FOO to the GUI thread.
</p>
<ol>
<li>
<p>The GUI thread services a
<samp class="codeph">WM_POP_FOO</samp> message by popping an item from
ResultQueue into CurrentResult. The
<samp class="codeph">try_pop</samp> always succeeds because there is exactly
one
<samp class="codeph">WM_POP_FOO</samp> message for each item in
<samp class="codeph">ResultQueue</samp>.
</p>
</li>
</ol>
<p>Routine
<samp class="codeph">LaunchLongRunningWork</samp> creates a root task and launches
it using method
<samp class="codeph">task::enqeueue</samp>. The task is a root task because it has
no successor task waiting on it.
</p>
<pre>class LongTask: public tbb::task {
HWND hWnd;
tbb::task* execute() {
Do long computation
Foo x = result of long computation
ResultQueue.push( x );
// Notify GUI thread that result is available.
PostMessage(hWnd,WM_POP_FOO,0,0);
return NULL;
}
public:
LongTask( HWND hWnd_ ) : hWnd(hWnd_) {}
};
void LaunchLongRunningWork( HWND hWnd ) {
LongTask* t = new( tbb::task::allocate_root() ) LongTask(hWnd);
tbb::task::enqueue(*t);
}</pre>
<p>It is essential to use method
<samp class="codeph">task::enqueue</samp> and not method
<samp class="codeph">task::spawn</samp>. The reason is that method
<samp class="codeph">enqueue</samp> ensures that the task eventually executes when
resources permit, even if no thread explicitly waits on the task. In contrast,
method
<samp class="codeph">spawn</samp> may postpone execution of the task until it is
explicitly waited upon.
</p>
<p>The example uses a
<samp class="codeph">concurrent_queue</samp> for workers to communicate results
back to the GUI thread. Since only the most recent result matters in the
example, and alternative would be to use a shared variable protected by a
mutex. However, doing so would block the worker while the GUI thread was
holding a lock on the mutex, and vice versa. Using
<samp class="codeph">concurrent_queue</samp> provides a simple robust solution.
</p>
<p>If two long computations are in flight, there is a chance that the
first computation completes after the second one. If displaying the result of
the most recently requested computation is important, then associate a request
serial number with the computation. The GUI thread can pop from
<samp class="codeph">ResultQueue</samp> into a temporary variable, check the
serial number, and update
<samp class="codeph">CurrentResult</samp> only if doing so advances the serial
number.
</p>
</div>
</div>
<div class="familylinks">
<div class="parentlink"><strong>Parent topic:</strong> <a href="../../tbb_userguide/Design_Patterns/Design_Patterns.htm">Design Patterns</a></div>
</div>
<div class="See Also">
<h2>See Also</h2>
<div class="linklist">
<div><a href="Non-Preemptive_Priorities.htm#Non-Preemptive_Priorities">Non-Preemptive Priorities
</a> provides informtion on how to implement priorities
</div>
<div><a href="Local_Serializer.htm#Local_Serializer">Local Serializer
</a> provides information on how to force serial ordering of certain
tasks
</div></div>
</div>
</body>
</html>
|