Introduction
Silverlight is Microsoft's important RIA tool whether in the Web application developing area or in the desktop scenario. In fact, since Silverlight for
Windows Phone 7 was born last year, Silverlight has been attracting more and more developers' eyes. As its direct competitor Flash, Silverlight, since its
first version, has provided support for multithread based computing. On the whole, the fundamental motive of introducing multithreading in Silverlight is to
improve the user experience to solve the user experience of responsiveness, by reducing the congestion of single threaded.
In this article, I would
like to sum up the several multi-threaded programming techniques in Silverlight 4 as comprehensively as possible. And, as usual, I'll try to bring you the
related sample code scraps.
NOTEThe sample test environments in this article involve:
1. Windows 7;
2. .NET 4.0;
3. Visual Studio 2010 (with
Silverlight 4).
Introducing the Thread Class
The Thread class is the first thing you should know in multithreading programming in Silverlight. There are many members defined in the Thread class.
However, we are not to enumerate them, but let a concrete example to show you the basic usage of the important ones.
As is seen above, to use the Thread class, we should begin by creating a new Thread object, at which point we supply a delegate to the method
to invoke asynchronously. In this case, DoWork is the method performed by a background thread (here the delegate type omitted). Note the
ThreadStart delegate can not take parameters, while the ParameterizedThreadStart delegate can be parameterized. Later examples
will show the related usage. Second, the IsBackground property specifies whether this is a background thread (for Silverlight, it did not
distinguish whether or not it is a background thread). The Start method is used to start the thread, passing an integer to specify the sleeping
time (millisecond). Note the Start() method returns immediately, and the related code begins executing asynchronously on a new thread. In fact,
we can pass any object to the Start method. Refer to the following definition:
Also note that, the method Join() is used to block the calling thread (in this case the main thread), until the specified thread
(in this case thread) is finished. If the specified thread is finished then continue to execute the subsequent statements; if the specified thread runs
longer than the specified time, then also continue. The return value specifies, within the specified time, whether or not the specified thread is finished
executing.
Now, let's consider the following really interesting thing:
The above code will fail – a running-time UnauthorizedAccessException will be thrown warning "Invalid cross-thread access". This shows the
thread isn't allowed to access Silverlight objects. This, in fact, presents a typical case under the multithreading environment. To solve this problem, you
are generally suggested to rest upon the System.Windows.Threading.Dispatcher object.
Using System.Windows.Threading.Dispatcher
Before diving into the System.Windows.Threading.Dispatcher object, let's first see how to tackle the above problem.
Run the above code again, you will find the value of the Content property of the Button control is modified successfully. To gain a more
modular design style, you can also express the same functionality as below:
As in many other frameworks, threads in Silverlight are also divided into two categories: UI thread and worker thread. Silverlight UI thread is
the thread interacting with the user. In the UI thread, there are the UI control classes and the ViewModel classes targeting data binding. And according to
the design Silverlight framework, a background worker thread cannot directly access the properties of data objects and controls in the UI thread. But there
is no need to worry about this. The threading model in Silverlight has leveraged a secure event based dispatcher model that is similar to EDT (Event Dispatch
Thread) in Java Swing, with which we can also easily succeed in data exchange between the UI thread and the background worker thread.
We know that all
Silverlight controls inherit from the base DependencyObject class. What deserves noticing is the DependencyObject class not only provides the fundamental
dependency property service for Silverlight but also opens a channel for data exchange between the UI thread and the background worker thread.
DependencyObject has a very important property – Dispatcher. So, the background thread can call the Dispatcher object of the launcher (mostly is the UI
control) to achieve interoperability. The following illustrates a typical schema for operation:
The above () => is a Lambda Expression representing the short form of the delegate method without incoming parameters. If
there are incoming parameters we can specify them in the parentheses.
Deployment.Current.Dispatcher
Till now, the preceding samples take place on the premise that the UI controls have been launched. As we've known, ordinarily the
Application.Current.RootVisual.Dispatcher property is used to retrieve the System.Windows.Threading.Dispatcher for an application. However,
this won't work if performed before the RootVisual has been assigned. To get the Dispatcher for an application before the RootVisual has been assigned, we
should resort to the System.Windows.Deployment.Current.Dispatcher object.
And also, in the case of a .dll library, we can use
Deployment.Current.Dispatcher to obtain the reference to Dispatcher. For instance, to change the value of the Text property of a
Silverlight control in an UI thread, you can use the following code:
BTW, here is a good example using Deployment.Current.Dispatcher for your reference.
About SynchronizationContext
SynchronizationContext seems to be mysterious stuff compared with the preceding things. According to MSDN, it "provides the basic functionality for propagating a synchronization
context in various synchronization models." According to my searching results from the Internet, the conclusion should be: the invention of
SynchronizationContext aims to simplify the synchronization provided you are sure enough that you are on the UI thread; or else, it will return a
null. So, in most cases, you can use Deployment.Current.Dispatcher as a replacement. As for using SynchronizationContext, it is not difficult. For example,
you also can achieve the similar operation as above with the synchronization context, as follows.
Note Daniel
Vaughan provides a good encapsulation with SynchronizationContext; cute readers can do more related digging.
ThreadPool
In all multithreaded solutions, ThreadPool should be your most commonly used technique. The advantage of using ThreadPool is apparent: 1, it is easy to
control as well as powerful, helping to reduce the whole cost concerning multithread coding; 2, in the thread pool a thread will not extinct due to the end
of a task, but will continue to perform other tasks, greatly reducing thread creation and destruction overhead. ThreadPool provides an important method -
QueueUserWorkItem which can push any processing function into a background thread executed in the queue. And also, its creation is also pretty
simple.
In fact, you will find there is not much difference in performing the same task compared with the above approach.
Now, let's look more closely at the usage of ThreadPool. Here we will construct two samples: one still relates to the method
QueueUserWorkItem; the other relates to another method RegisterWaitForSingleObject.
First, let's put two TextBlock controls
on the sample page ThreadPoolTestPage.xaml:
When clicking the either of the two labels, a related sample process will be launched. Let's continue what happens in the behind
code:
For the first method QueueUserWorkItem, we just notice two points. First is the parameter definition.
Listing 1: There are two overloads for the method QueueUserWorkItem
Here the first parameter specifies the method to execute, while the second parameter is used as argument passed to the method. Second, the
method executes only when the thread becomes available in the thread pool. As you would image, as part of thread management strategy, there is a certain
degree of delay before thread pool creates the thread. This is why we invoke the Thread.Sleep method.
For the second method
RegisterWaitForSingleObject, there are some complexities. First, we set up an AutoResetEvent instance, passing in the false parameter. Second,
we introduce a helper class RegisteredWaitHandlePacket with which to encapsulate another RegisteredWaitHandle class. There are several parameters for the
method RegisterWaitForSingleObject. However, we are not going to dwell upon them since MSDN has given more detailed explanation. And also, we used the delay policy for the second
method.
NOTEAlthough the ThreadPool provides two methods, SetMinThreads and SetMaxThreads, but not available - calling
them will trigger an exception.
About WaitHandle
A wait handle is the essential equipment for multithreaded programming. Since our main interest lies in the Silverlight related multithreading stuff, we
are not to delve into WaitHandle, but bring you a typical example to tell how to use it.
Timer
System.Thread.Timer is a multi-threaded timer. It is a simple, lightweight timer that uses callback methods and is served by thread pool threads. Let's
look at a related example:
Listing 2: (see the sample page TimerTestPage.xaml)
There are several points deserved noticing here. First, make clear the parameters passed to Timer class: the method MyTimerCallback
represents a method to be executed in the thread pool; the second parameter (a string in this case) means data passed to the method
MyTimerCallback; the third parameter specifies the amount of time to delay before the method MyTimerCallback is invoked, in
milliseconds; the forth one specifies the time interval between invocations of the method MyTimerCallback, in milliseconds. Second, we've used
the SynchronizationContext object since the threading context is clear to track. Also note, inside the method MyTimerCallback we called the
Post method of it to modify the content in the UI thread. Last, through the method Change of Timer we specify that after the
method executes 5 times, change the start time to five minutes and the interval to five milliseconds between method invocations for the timer.
System.Windows.Threading.DispatcherTimer
DispatchTimer first appears in Silverlight (WPF), as a background thread timer. Compared with the original System.Threading.Timer, the difference lies in
that DispatchTimer is truly executed independently within a background thread, while the Timer is still executed in the UI thread but every specified time
takes over the right to control the UI thread. DispatchTimer is only suitable for scheduled tasks. We can set the waiting break according to factual
requirements.
In fact, DispatcherTimer is also an important component to achieve animation except for the StoryBoard component. Of course, we should be
careful with the DispatcherTimer to build too many the background threads; otherwise they will result in increasing the CPU scheduling overhead to decrease
efficiency.
BackgroundWorker - the Lazy Way
System.ComponentModel.BackgroundWorker was first introduced with .NET 2.0 to simplify the coding process of thread interactions in Windows Forms
applications. Now, it can also be used in the Silverlight environment. Behind the scenes, BackgroundWorker uses the Dispatcher component and encapsulates all
the multithreading intricateness inside a black box, providing you a most easy-to-use and ready-to-use solution. On the whole, BackgroundWorker is pretty
suitable for undertaking a single, asynchronous, and long task that runs in the background.
Peeking result via .NET Reflector
Now, let's first look at the declaration of the BackgroundWorker class using .NET Reflector.
Implied from the names, you will easily image most of the members are commonly used. For brevity, we will no more delve into the related
explanations. However, we will construct a concrete example to look at a typical case using BackgroundWorker.
On the whole, BackgroundWorker is suitable to run an operation on a separate thread, especially to run a time-consuming operation on a non-UI
thread to prevent from UI stopping response. Above, we used a 'Cancel' button to cancel the thread executing by judging whether the thread allows cancelled
(through the property WorkerSupportsCancellation). And then, we invoke the method CancelAsync to suspend the thread.
Correspondingly, the method RunWorkerAsync is used to start the thread, passing the possible required parameters. As is seen, the real
asynchronous work is done in the method _backgroundWorker_DoWork. At the same time, it reports the progress using the method
ReportProgress; a related method _backgroundWorker_ProgressChanged is responsible to render the progress on the UI thread. As soon
as the thread terminates or is suspended another related method _backgroundWorker_RunWorkerCompleted will invoked and output the related prompt
info.
Summary
In this article, we've summed up the typical ways to incorporate multithreading into a Silverlight application. However, writing a multithreaded
Silverlight application is not as easy as covered herein – we've just scratched the surfaces. Hence, before putting multithread programming into your real
work, you are highly suggested considering the advice of Microsoft. Frankly speaking, to choose multithreading means in a great degree you have to face up
with locking complications and synchronization mechanisms. Whether to introduce multithreading into your Sivlerlight projects or not should depended on your
final decision.
About Xianzhong Zhu
 |
I'm a college teacher and also a freelance developer and writer from WeiFang China, with more than fourteen years of experience in design, and development of various kinds of products and applications on Windows platform. My expertise is in Visual C++/Basic/C#, SQL Server 2000/2005/2008, PHP+MyS...
This author has published 81 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Building A Product For Real
read more
Silverlight MVP
read more
Mixing Silverlight and MS ASP.NET AJAX 3.5 in the same web application.
read more
In Response To DNR 476
read more
Building a Silverlight 3 based RIA Image Magagement System (1)
read more
Building a Silverlight 3 based RIA Image Management System - 1
read more
Telerik Launches RadControls for Silverlight 3 for Line-of-Business Application Development
read more
Provide startup parameters to Silverlight with InitParams
read more
Silverlight Twitter Client with authentication
read more
OMG, where do I start?
read more
|
|
Please login to rate or to leave a comment.