If you like to write applications that annoy your users, a good way to do this is to make the user interface stop responding to input from time to time. For extra frustration, you can compound the problem by not giving any visible indication that work might be progressing, leaving the user to wonder whether the application is busy or has simply crashed. Because you’ll get this behavior by default if you don’t take certain steps to maintain responsiveness, you can stop reading now. Unless, that is, you’d prefer not to annoy your users.
Unfortunately, it’s all too easy to write your application in such a way that it becomes unresponsive when it performs time-consuming work such as accessing a server over a network or reading files off disk. In Windows, all messages regarding user input for a particular window are delivered to the same thread. In general, this is a good thing, because it means your code has to deal with input events only one at a time, and does not have to worry about thread safety. The downside is that the application can respond to input only if this thread is available to process it.
Many APIs are synchronous—they
do not return until they have completed the work you asked them to
perform. (Such APIs are said to block.) If you write a handler for a button’s
Click
event that makes a synchronous
API call to retrieve data from a database, that call will not return until
the database returns the data. The thread cannot do anything else until
that synchronous call returns, so the application will be unable to
respond to any other user input for the duration of the call.
Even if you avoid synchronous APIs, you could still cause sluggishness simply through slow code. Code risks being slow if it performs either CPU-intensive or memory-intensive work. Slow CPU-intensive work is fairly uncommon—computers are fast enough these days that you need to find a considerable amount of work for processing to seem anything less than instantaneous, and only a handful of applications do this. However, excessive memory use is much more common, and it can have a drastic effect on speed, particularly once paging to disk occurs. If the OS has to load a page off disk back into memory, the amount of time this takes is long enough to execute tens of millions of instructions. This has to happen only a couple of times before it adds up to a perceptible delay. Whether code is slow due to memory or CPU usage, running slow code on the same thread that handles user input will make the UI unresponsive.
There are two ways to solve this problem. One is to use asynchronous APIs. Some parts of the .NET
Framework offer asynchronous invocation, where the API call returns
immediately without waiting for the work to complete. For example, instead
of using the Stream
class’s blocking
Read
method, you could call the
nonblocking BeginRead
, passing in a
callback to be notified when the read operation completes. Alternatively,
you can use multithreaded programming—if you execute code on some thread
other than the thread that handles input, it doesn’t matter whether this
other thread executes slow code or calls synchronous APIs, because the
input handling thread is free to respond to other user input.
Multithreaded programming in WPF works in the same way as in any other .NET application. Because this appendix deals only with multithreading issues specific to WPF applications, we won’t be showing any of the general-purpose .NET threading techniques. For more general information on .NET’s multithreading facilities, consult the “Managed Threading” topic in the SDK documentation at http://msdn2.microsoft.com/library/3e8s7xdd.aspx (http://tinysells.com/80).
Using asynchronous APIs often results in the use of multiple threads. Although you might not explicitly create any new threads, you may be notified of the completion of some asynchronous operation on a different thread from the one that started the work. So, regardless of whether you choose to use asynchronous APIs or multithreading to keep your application responsive, an understanding of WPF’s threading model will be necessary.
Many WPF objects have thread affinity, meaning that they belong to a particular thread. Your code must always use a WPF user interface element on the same thread that created it. It is illegal to attempt to use any WPF element from any other thread.
If you are familiar with Windows Forms, you will be used to this threading model. If you are familiar with COM (the Component Object Model), you will recognize this as resembling the Single Threaded Apartment (STA) model.
WPF uses this model for various reasons. One is simplicity—the model is straightforward and does not introduce any complications for applications that have no need for multiple threads. This simplicity also makes it fairly straightforward for WPF to detect when you have broken the rules so that it can alert you to the problem with an exception. There are also performance benefits: thread affinity avoids locking, which is usually required with multithreaded models, and locks add both complexity and performance overhead.
Another important reason for using a single-threaded model is to support interop with Win32, which also has thread affinity requirements. (It is not quite as strict, in that it is possible to perform many operations from the “wrong” thread. However, such operations are often handled very differently than the same operations performed on the right thread, and numerous pitfalls are associated with cross-thread window usage.) By adopting a strict thread affinity model, you can mix WPF, Windows Forms, and Win32 user interface elements freely within a single application.
You might be wondering how you can be sure which types have
thread affinity and which don’t. Although all user interface elements
have this requirement, it does not apply to every single type you use
in a WPF application. For example, built-in types not specific to WPF,
such as Int32
, do not care which
thread you use them on, as long as you use them from only one thread
at a time.
But what about types that are specific to WPF but are not user
interface elements, such as Brush
,
Color
, and Geometry
? How are we to tell which types
have thread affinity requirements? The answer is to look at the base
class. WPF types with thread affinity derive from the DispatcherObject
base class. Brush
and Geometry
both derive from DispatcherObject
, so usually you can use
them only on the thread that created them.[128] Color
does not, and
therefore you can use it on a different thread from the one on which
it was created.
The DispatcherObject
class
defines a few members, all of which are exempt from the thread
affinity rule—you can use them from any thread. Only the
functionality added by classes that derive from DispatcherObject
is subject to thread
affinity.
DispatcherObject
provides a
couple of methods that let you check whether you are on the right
thread for the object: CheckAccess
and VerifyAccess
. CheckAccess
returns true if you are on the
correct thread, false otherwise. VerifyAccess
is intended for when you think
you are already on the right thread, and it would be indicative of a
problem in the program if you were not. It throws an exception if you
are on the wrong thread. Example C-1 shows
a method that uses this to ensure that it has been called on the right
thread.
public void Frobnicate(FrobLevel fl) { // Ensure we're on the UI thread VerifyAccess( ); ... }
Many WPF types call VerifyAccess
when you use them. They do not
do this for every single public API, because such comprehensive
checking would impose a significant performance overhead. However, it
checks in enough places that you are unlikely to get very far on the
wrong thread before the problem becomes apparent.
If your application causes multiple threads to be created, either through explicit thread creation or implicitly through the use of asynchronous APIs, you should avoid touching any user interface objects on those threads. If you need to update the user interface as a result of work done on a different thread, you must use the dispatcher to get back onto the UI thread.
Each thread that creates user interface objects needs a Dispatcher
object. This effectively owns the
thread, running a loop that dispatches input messages to the appropriate
handlers. (It performs a similar role to a message pump in Win32.) As
well as handling input, the dispatcher enables us to get calls directed
through to the right thread.
The Dispatcher
class lives in
the System.Windows.Threading
namespace along with all other WPF-specific threading classes,
including DispatcherObject
.
Recall that all WPF objects with thread affinity derive from the
DispatcherObject
base class. This
class defines a Dispatcher
property, which returns the Dispatcher
object for the thread to which
the object belongs.
You can also retrieve the Dispatcher
for the current thread by using
the Dispatcher.CurrentDispatcher
static property.
If you need to update the user interface after doing some work
on a worker thread, you must make sure the update is done on the UI
thread. The Dispatcher
provides
methods that let you invoke the code of your choice on the
dispatcher’s thread.
You can use either Invoke
or
BeginInvoke
. Both of these accept
any delegate and an optional list of parameters. They both invoke the
delegate’s target method on the dispatcher’s thread, regardless of
which thread you call them from. Invoke
does not return until the method has
been executed, whereas BeginInvoke
queues the request to invoke the method, but returns straight away
without waiting for the method to run.
Invoke
can be simpler to
understand, because you can be certain of the order in which things
happen. However, it can lead to subtle problems—by making a worker
thread wait for the UI thread, there is a risk of deadlock. The worker
thread may be in possession of locks or other resources that the UI
thread is waiting for, and each will wait for the other indefinitely,
causing the application to freeze. BeginInvoke
avoids this risk, but adds the
complexity that the order of events is less predictable.
Example C-2 shows the use of
the dispatcher’s BeginInvoke
method. This is a typical way of structuring code that is not running
on the UI thread, but which needs to do something to the UI. In this
case, the code sets the background color of the window. The RunsOnWorkerThread
method in this example
runs on a worker thread. (The mechanism by which that worker thread
was created is not shown here, because the techniques used for
creating worker threads in WPF applications are exactly the same as
those used in any other .NET application.)
partial class MyWindow : Window { ... public delegate void MyDelegateType( ); void RunsOnWorkerThread( ){ Color bgColor = CalculateBgColor( ); MyDelegateType methodForUiThread = delegate { this.Background = new SolidColorBrush(bgColor); }; this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, methodForUiThread); } ... }
We’re using the C# anonymous delegate syntax here. You don’t
have to use this—you could just put the code in a separate method.
However, anonymous delegates are often particularly convenient in this
kind of scenario, because you can use any of the variables that are in
scope in the containing method. In this case, we are setting the
bgColor
variable in the containing
method, and then using that value in the nested anonymous method that
will run on the UI thread. This sharing of lexical scope across two
methods makes moving from one thread to another relatively
painless.
The first parameter passed to BeginInvoke
indicates the priority with
which we would like the message to be handled. The Dispatcher
does not operate a strict “first
in first out” policy, because some messages need to be handled with
higher priority than others. For example, suppose two work items are
queued up with the dispatcher, where one is a message representing
keyboard input and the other is a timer event that will poll some
remote service for status. The remote polling is likely to take a
while to complete, so delaying the poll a little won’t have any
visible effect. However, even fairly small delays in processing user
input tend to make an application feel unresponsive, so you would
normally want the key press to be handled before background tasks such
as polling. The dispatcher therefore handles messages according to
their specified priority to allow those that are sensitive to latency,
such as input messages, to be handled ahead of less urgent
tasks.
The Normal
priority level is
relatively high—it runs ahead of input processing and even rendering.
For quick operations, this is not a problem, but in some cases you may
want the work to run as a BackgroundWorker
operation—something that will run only when there is nothing more
important to do. For this kind of processing, use either the ApplicationIdle
or the SystemIdle
priority level. ApplicationIdle
will not process the message
until the application has nothing else to do. SystemIdle
considers activity across the
whole machine, and processes the message only when a CPU would
otherwise be idle.
The second parameter to BeginInvoke
is the delegate. The Dispatcher
will invoke this at some point in
the future on the dispatcher thread. If we had used a delegate type
that required parameters to be passed to the target function, we would
have used one of the overloads of BeginInvoke
that accepts extra parameters,
as Example C-3
shows.
delegate void UsesColor(Color c);
void SetBackgroundColor(Color c) {
this.Background = new SolidColorBrush(c);
}
void RunsOnWorkerThread( ) {
UsesColor methodForUiThread = SetBackgroundColor;
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, methodForUiThread,
Colors.Blue
);
}
Here we have defined a custom delegate type called UsesColor
. It requires its target function
to take a single parameter of type Color
. The delegate was defined to match the
signature of SetBackgroundColor
,
the method we want to call. This method sets the window background
color, so it needs to run on the UI thread. We’re assuming that the
RunsOnWorkerThread
method isn’t on
the right thread, so it uses the Dispatcher.BeginInvoke
method to call
SetBackgroundColor
on the correct
thread.
However, there are a couple of differences between Example C-2 and Example C-3. One is cosmetic—we
are no longer using the C# anonymous delegate syntax. The other is
that we are now passing an extra parameter to BeginInvoke
. You can pass as many extra
parameters as you like—one of the BeginInvoke
overloads accepts a variable
length argument list. All of these parameters will be passed into the
target function on the dispatcher thread.
If you are familiar with the .NET asynchronous pattern, you
might be wondering whether there is an EndInvoke
method. Typically, any call to a
Begin
Xxx
method has to be matched with a corresponding End
Xxx
call. But
the Dispatcher
does not use the
standard asynchronous pattern. BeginInvoke
has no corresponding EndInvoke
method, nor does it provide a way
of passing in a completion callback function to BeginInvoke
as you would expect to see with
a normal implementation of the .NET asynchronous pattern. However, it
is possible to discover when an operation is executed by using the
DispatcherOperation
class. This
class also supports cancellation, which is not available in the
standard asynchronous pattern.
The Dispatcher.BeginInvoke
method returns a DispatcherOperation
object. This represents
the work item sent to the dispatcher. You can use it to determine the
current status of the operation. Its Status
property will be one of the values
from the DispatcherOperationStatus
enumeration, shown in Table C-1.
Value | Meaning |
| The dispatcher has not yet called the method. |
| The method is currently executing on the dispatcher thread. |
| The method has finished executing. |
| The operation was aborted. |
You will see the Aborted
status only if you cancel the operation. You can cancel an operation
by calling the DispatcherOperation.Abort
method. As long as
the operation has not already started, this removes it from the
dispatcher’s queue. This method returns true if the operation was
cancelled, and false if it had already started by the time you called
abort.
You can wait for the operation to complete by calling the
Wait
method. This blocks the worker
thread until the UI thread has executed the method. (This carries the
same risk of deadlock as Invoke
.)
Alternatively, you can add a handler to the Completed
event, which will be raised when
the method completes. However, this is slightly tricky to use, because
it’s possible that the operation will already have run by the time you
get around to adding the handler. It may be simpler just to write your
code in a way that avoids using either of these. Remember that
BeginInvoke
calls the method you
tell it to. If you need to do some work after the dispatcher has
called your code, just add that to the method, as Example C-4 shows.
MyDelegateType work = delegate { DoWorkOnUIThread( ); DoWhateverWeNeedToDoNowTheMainWorkHasBeenDone( ); }; this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, work);
Of course, both methods called in Example C-4 will run on the UI thread. If the second method is slow, just use a suitable multithreading or asynchronous invocation mechanism to move it back onto a worker thread.
When you call BeginInvoke
,
the dispatcher will run your method as soon as it is able to. If the
UI thread is idle, this will happen immediately. This is not always
desirable—it can be useful to be called back after a delay, which is
what makes the dispatch timer useful.
Applications often create timers in order to perform
housekeeping tasks on a regular basis. You could use either of the
Timer
classes in the .NET class
library, but both of these would notify you on a thread from the CLR
thread pool, meaning you’d have to call Dispatcher.BeginInvoke
to get back onto the
right thread.
It is simpler to use the WPF-aware DispatcherTimer
class. This raises timer
notifications via the dispatcher, meaning your timer handler will
always run on the correct thread automatically. This enables you to do
things to the user interface directly from the handler, as Example C-5 shows.
partial class MyWindow : Window { DispatcherTimer dt; public MyWindow( ) { dt = new DispatcherTimer( ); dt.Tick += dt_Tick; dt.Interval = TimeSpan.FromSeconds(2); dt.Start( ); } Random rnd = new Random( ); void dt_Tick(object sender, EventArgs e) { byte[] vals = new byte[3]; rnd.NextBytes(vals); Color c = Color.FromRgb(vals[0], vals[1], vals[2]);// OK to touch UI elements, as the DispatcherTimer
// calls us back on the UI thread
this.Background = new SolidColorBrush(c);
} }
By default, the DispatcherTimer
uses the Background
priority level to deliver
notifications. If necessary, you can change this by passing in a value
from the DispatcherPriority
enumeration when you construct the timer. You can also pass in a
Dispatcher
, although by default it
will use the Dispatcher.CurrentDispatcher
property to
retrieve the dispatcher for the current thread. However, you will need
to pass the dispatcher explicitly if you are creating the timer from a
different thread than the UI thread.
It is not strictly necessary for there to be just one UI thread—it is possible for an application to create user interface objects on several threads. However, all of the elements in any given window must belong to the same thread. So, in practice, you can have at most one UI thread per top-level window.
It is fairly rare to use more than one UI thread—the user can interact with only one window at a time, so there is normally no need for concurrent dispatchers. However, if for some reason, you cannot avoid blocking the UI thread, it might be appropriate to use multiple UI threads in order to localize the blocking to a single window. For example, suppose you need to host an unreliable or slow third-party UI component. Using one thread per top-level window would mean that if the component should freeze, it would take out only one window rather than the whole application. Internet Explorer uses multiple UI threads for this very reason. (Of course, IE isn’t a WPF application, but the same principle applies to Win32 applications.)
Each thread that hosts UI objects needs a dispatcher in order
for those UI objects to function. In a single-threaded application,
you don’t need to do anything special to create a dispatcher. The
Application
class creates one for
you at startup, and shuts it down automatically on exit. However, if
you create multiple user interface threads, you will need to start up
and shut down the dispatcher for those manually. Example C-6 shows how to
start a dispatcher.
void StartDispatcher( ) { Thread thread = new Thread(MyDispatcherThreadProc); thread.SetApartmentState(ApartmentState.STA); thread.Start( ); } void MyDispatcherThreadProc( ) { Window1 w = new Window1( ); w.Show( ); // Won't return until dispatcher shuts down System.Windows.Threading.Dispatcher.Run( ); }
The Dispatcher
for a thread
is created automatically the first time an object derived from the
DispatcherObject
base class is
created. All WPF classes derive from this base class. So, the Dispatcher
for the new thread will come into
existence when the Window
is
created. All we have to do is call the static Dispatcher.Run
method to ensure that
messages are delivered to any UI objects created on the thread. This
method will not return until you call InvokeShutdown
or BeginInvokeShutdown
on the
dispatcher.
WPF will call InvokeShutdown
for you on the dispatcher
it creates for the application’s main thread. However, it is your
responsibility to call this method for any other thread on which you
call Dispatcher.Run
. If you fail
to do this, your application will continue to run even after the
Application
object shuts down the
main thread.
The Dispatcher
requires that
you set the COM threading model to STA. Although a thread’s COM
threading model is used only in COM interop scenarios, many system
features rely on COM interop under the covers. The Dispatcher
therefore requires the model to
be set even if your application does not use any COM components
directly. The call to SetApartmentState
in Example C-6 ensures that the
correct model is used.
Although WPF does support the use of multiple UI threads, it does not support UI in multiple AppDomains. All the UI threads in a given process must be in the same AppDomain.
Some components allow you to perform asynchronous work without
having to worry about the details of the dispatcher. This is possible
thanks to the event-based asynchronous
pattern, which was introduced in NET 2.0. Components that
implement this pattern manage the necessary thread switching for you.
They do so using the AsyncOperationManager
family of
classes,[129] which abstract away the details of UI threading
requirements, supporting both Windows Forms and WPF through a common
API. This means that classes designed for use in Windows Forms
applications will also work correctly in WPF applications.
The event-based asynchronous pattern is fairly simple. A class
will provide one or more methods whose names end in Async
. For example, the WebClient
class in the System.Net
namespace offers an UploadFileAsync
method. Each asynchronous
method has a corresponding event to signal completion—UploadFileCompleted
, in this case. There may
optionally be other events to indicate partial progress, such as the
UploadProgressChanged
event offered
by WebClient
. The crucial feature of
the event-based asynchronous pattern is that the events are raised on
the UI thread. For example, if you call UploadFileAsync
from the UI thread of a WPF
application, the object will raise the UploadFileCompleted
event on the same
thread.
Not all components offer this pattern. Fortunately, .NET provides
an implementation of the pattern that you can use to wrap slow,
synchronous code: the BackgroundWorker
class.
The BackgroundWorker
class is
defined in the System.ComponentModel
namespace. It makes it
easy to move slow work onto a worker thread in order to avoid making
the UI unresponsive. It also provides a very simple way of sending
progress and completion notifications back to the UI thread. It uses
the AsyncOperationManager
internally to implement the event-based asynchronous pattern, so in a
WPF application it will use the Dispatcher
under the covers when raising
events.
Example C-7 shows the BackgroundWorker
class in use. We start by
attaching a handler to the DoWork
event. This event will be raised on a worker thread, so we can do slow
work in this event handler without causing the UI to become
unresponsive. This example also handles the ProgressChanged
and RunWorkerCompleted
events. Your code can
cause these to be raised to indicate that the work is progressing or
has completed. Note that you will not get ProgressChanged
events automatically. First,
you must enable them by setting the WorkerReportsProgress
property to true.
Having enabled them, they will be raised only if the DoWork
handler calls the ReportProgress
method from time to
time.
partial class MyWindow : Window { BackgroundWorker bw; public MyWindow( ) {bw = new BackgroundWorker( );
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync( );
} void bw_DoWork(object sender, DoWorkEventArgs e) { // Running on a worker thread for (int i = 0; i < 10; ++i) { int percent = i * 10; bw.ReportProgress(percent); Thread.Sleep(1000); } // The BackgroundWorker will raise the RunWorkerCompleted // event when this method returns. } void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Running on a UI thread this.Title = "Working: " + e.ProgressPercentage + "%"; } void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // Running on a UI thread this.Title = "Finished"; }
When we call the RunWorkerAsync
method, the BackgroundWorker
raises the DoWork
event on a worker thread. This means
the DoWork
handler can take as long
as it likes, and will not cause the UI to freeze. Of course, it must
not do anything to the user interface because it is not on the right
thread. However, the ProgressChanged
and RunWorkerCompleted
events will always be
raised on the UI thread, so it is always safe to use UI objects from
these.
The RunWorkerCompleted
handler is passed a RunWorkerCompletedEventArgs
object. If there
is a possibility that your DoWork
method might throw an exception, you should check the Error
property of this object. It will be
null
if the work completed
successfully, and it will contain the exception otherwise.
[128] * It’s slightly more complex than
that for these particular types. Brush
and Geometry
derive from DispatcherObject
indirectly via the
Freezable
base class. This
means they can be frozen, which has the effect of detaching them
from their dispatcher and removing the thread affinity
requirement. We discuss freezing in more detail in Appendix D.