Getting back to the UI Thread in Silverlight 2 Beta 2

The problem

Silverlight 2 Beta 2 has changed the concurrency model for asynchronous communications.  In Silverlight 2 Beta 1,  asynchronous requests always returned on the UI Thread.  This was convenient,  since updates to the user interface can only be done via the UI thread.  As of Silverlight 2 Beta 2,  asynchronous callbacks are fired in worker threads that come from a thread pool:  although this potentially allows for better performance via concurrency,  it increases the risk for race conditions between callbacks –  more importantly,  some mechanism is necessary to make code that updates the user interface run in the UI thread.

A solution

It’s straightforward to execute a function in the UI thread by using the Dispatcher property of any ScriptObject The tricky part is that ScriptObjects are part of the user interface,  so you can only access the Dispatcher property from the UI thread.  At first this seems like a chicken-and-egg situation:  you need a Dispatcher to get to the UI thread,  but you need to be in the UI thread to get a Dispatcher.

This dilemma can be resolved by accessing a Dispatcher in your App.xaml.cs file and stashing it away in a static variable on application startup:

private void Application_Startup(object sender, StartupEventArgs e) {
    ...
    UIThread.Dispatcher = RootVisual.Dispatcher;
}

UIThread is a simple static class:

public static class UIThread {
    public static Dispatcher Dispatcher {get; set;}
    public static void Run(Action a) {
        Dispatcher.BeginInvoke(a);
    }
}

At some point in the future,  you can transfer execution to the UIThread by scheduling a function to run in it.

public class ProcessHttpResponse(...) {
   ...
   UIThread.Run(UpdateUserInterface);
}

The parameter of Run is an Action delegate,  which is a function that returns void and takes no parameters.  That’s fine and good,  but what if you want to pass some parameters to the function that updates the UI.  The usual three choices for passing state in asynchronous applications apply,  but it’s particularly easy and fun to use a closure here:

public class ProcessHttpResponse(...) {
    ...
    var elementToUpdate=...;
    var updateWithValue=...;

    UIThread.Run(delegate() {
        UpdateUserInterface(elementToUpdate,updateWithValue)
    });
}

When to return?

If your application is complex,  and you have nested asynchronous calls,  you’re left with an interesting question:  where is the best place to switch execution to the UI thread?  You can switch to the UI Thread as soon as you get back from an HttpRequest or a WCF call and you must switch to the UI Thread before you access any methods or properties of the user interface.  What’s best?

It is simple and safe to switch to the UI Thread immediately after requests return from the server.  If you’re consistent in doing this,  you’ll never have trouble accessing the UI thread,  and you’ll never have trouble with race conditions between returning communication responses.  On the other hand,  you’ll lose the benefit of processing responses concurrently,  which can improve speed and responsiveness on today’s multi-core computers.

It’s straightforward to exploit concurrency when a requests can be processed independently.  For instance,  imagine a VRML viewer written in Silverlight.  Displaying a VRML would require the parsing of a file,  the construction of the scene graph and the initialization of a 3-d engine,  which may require building data structures such as a BSP Tree.  Doing all of this work in the UI Thread would make the application lock up while a model is loading — it would be straightforward,  instead,  to construct all of the data structures required by the 3-d engine,  and attach the fully initialized 3-d engine to the user interface as the last step.  Since the data structures would be independent of the rest of the application,  thread safety and liveness is a nonissue.

Matters can get more complicated,  however,  if the processing of a request requires access to application-wide data;  response handlers running in multiple threads will probably corrupt shared data structures unless careful attention is paid to thread safety.  One simple approach is to always access shared data from the UI Thread,  and to transfer control to the UI Thread with UIThread.Run before accessing shared variables.

Conclusion

Silverlight 2 Beta 2 introduces a major change in the concurrency model for asynchronous communication requests.  Unlike SL2B1,  where asynchronous requests executed on the user interface thread,  SL2B2 launches asynchronous callbacks on multiple threads.  Although this model offers better performance and responsiveness,  it requires Silverlight programmers to explicitly transfer execution to the UI thread before accessing UI objects:  most SL2B1 applications will need to be reworked.

This article introduces a simple static class,  UIThread,  which makes it easy to schedule execution in the UI Thread.

kick it on DotNetKicks.com