blog comments 0 del.icio.us bookmarks 0 diggs 0 Google results 0

1.0
PostRank

Threading in Silverlight

From Wilco Bauwer, 7 months ago, 0 views

Silverlight 2 brings support for threading to the browser. You can either directly start new threads using System.Threading.Thread and System.Threading.ThreadPool, or you can use the higher-level (and recommended) System.ComponentModel.BackgroundWorker type. The latter encapsulates the concept of executing work in the background (using a thread from the thread-pool) and updating the UI based on progress and/or completion of that work, which means that you can safely update the UI from the related events.

A lesser-known type that we introduced in beta 1 is System.Windows.Threading.Dispatcher. This type lets you execute work on the UI thread - something that's useful when you directly want to update the UI from a background thread. Since Silverlight always has a single UI-thread, there is only a single disatcher instance per Silverlight application. This instance is accessible via any DependencyObject or ScriptObject instances' Dispatcher property. Once you have a reference to a dispatcher, you can use its BeginInvoke method to dispatch your work. In Silverlight we added an overload which takes an Action, which means you don't need to add a cast or anything to help the compiler infer what type of delegate you want to pass:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

var myThread = new Thread(() => {
    // Using a lambda...
    myTextBlock.Dispatcher.BeginInvoke(() => myTextBlock.Text = "Updated from a non-UI thread.");

    // Using an anonymous delegate...
    myTextBlock.Dispatcher.BeginInvoke(delegate {
        myTextBlock.Text = "Updated from a non-UI thread.";
    });
});
myThread.Start();

Please note that you may not be able to find the dispatcher property via intellisense. It's marked as an advanced property, so you either need to update your VS settings to display advanced members, or you just need to ignore intellisense and assume your code will in fact compile regardless of what intellisense implies. The same goes for CheckAccess, which is actually marked as a member that should never be displayed. The main reason these members aren't always visible is because they shouldn't be as common as the other members on a DependencyObject. As I mentioned before, you'll probably want to use a BackgroundWorker most of the time instead.

Gotchas

There are a couple of things to be aware of. The first is that we try to guard against cross-thread invocations when this would potentially be unsafe. For example, we don't allow you to call into the HTML DOM or a JavaScript function from a background thread. The reason for this is that both assume to be invoked on the UI thread. Breaking this assumption can lead to unexpected behavior, including browser crashes.

The other thing to be aware of is creating deadlocks. Silverlight comes with primitives such as Monitor (encapsulated via the lock construct in C#) and ManualResetEvent which make it trivial to create a deadlock. A deadlock will cause most browsers to hang completely. While technically this isn't very different from some JavaScript that infinitely, it's often easier to accidentally create a deadlock than an infinite loop of code. For example, I've seen several people try to create a synchronous version of HttpWebRequest by letting the current thread wait for a ManualResetEvent to be notified by the response callback. HttpWebRequests however execute their callbacks on the UI thread, which means you have a deadlock right there. While ideally you avoid blocking the UI thread entirely, you should at least consider specifying timeouts when you use a synchronization object. For example, instead of the lock construct in C# (Monitor.Enter/Exit), consider using Monitor.TryEnter/Exit passing in a reasonable timeout, and instead of using ManualResetEvent's parameterless WaitOne, consider using one of the overloads.

comments

No comments yet.

You must be logged in to add your own comment.