For Now, Don't Use SynchronizationContext in Silverlight 2 or 3

Silverlight Logo

In Silverlight (and WPF before) I became enamoured with the Dispatcher for marshaling data back to the UI thread. It works, its simple (and throw in lambda's in C# and its super easy). But I've been trying to be a good citizen lately. It seems that many people are using SynchronizationContext to do this instead. The SynchronizationContext class is available in Silverlight, WPF and in Windows Forms so it felt like its what I should be using so that code I write would be portable.

So in a recent project I was writing for an article, I was using it but it simply failed and had me perplexed.  Maybe I was using it wrong?  Maybe it didn't work with lambda expressions?  Maybe I needed to reboot my machine?  It just felt wierd.

As is my style, I took the problem out of the project and built a quick tester so I could see if I could replicate the problem (something I advocate heavily).

So I wrote a little tester (you can get it from the link above if you're interested) to see how the Dispatcher and SynchronizationContext worked in different situations. In my tester, I tested both the Dispatcher and the SynchronizationContext object from the UI thread, from a ThreadPool thread, a BackgroundWorker thread and from a custom built Thread.   

In the Dispatcher examples, I am getting at the Dispatcher through the Deployment.Current object which is how you should do it since UI elements with Dispatcher won't like being called from a separate thread.  For example, using a ThreadPool thread, I call the Dispatcher (like so:

ThreadPool.QueueUserWorkItem(new WaitCallback(
  o =>
  {
    try
    {
      Deployment.Current.Dispatcher.BeginInvoke(
        () => results.Text = "Pooled Dispatcher Worked");
    }
    catch (Exception ex)
    {
      Debug.WriteLine("Pooled Dispatcher - Error occurred: {0}", ex);
    }
  }));

And the SynchronizationContext like so:

ThreadPool.QueueUserWorkItem(new WaitCallback(
  o =>
  {
    try
    {
      SynchronizationContext.Current.Post(
        new SendOrPostCallback(
          ignore => results.Text = "Pooled Synchronization Worked"), null);
    }
    catch (Exception ex)
    {
      Debug.WriteLine("Pooled SyncContext - Error occurred: {0}", ex);
    }
  }));

I found odd behavior. The SynchronizationContext.Current (the property that you use to do the marshaling) was null unless you were on the UI thread.  Wait, did I just type that?  The very property that you need to use when you're *not* on the UI thread isn't available unless you're on the UI thread?  Yup, that appears to be the situation.

I also tested this against the current Silverlight 3 build and its behaves exactly the same way.

So for now, (unless I just missed something big) I suggest sticking with Dispatcher we find out why this is happening.

UPDATE: As several have pointed out, you need to cache the SynchronizationContext.Current and pass it along to the non-UI thread. It is working as designed, but for Silverlight I am sticking to the Dispatcher as it has fewer moving parts.

Comments:

Gravatar

I think that is the way that it is supposed to be. You are responsible for capturing the current SynchronizationContext instance from the thread that you want to marshal back to (in this case the UI thread) and saving it for future reference. Then use that saved reference when you need to marshal data back to the UI thread. I believe that this is by design since you are able to derive custom SynchronizationContext objects and setting the current one per thread.

Gravatar

Try this:

SynchronizationContext context = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(new WaitCallback(
o =>
{
try
{
context.Post(
new SendOrPostCallback(
ignore => results.Text = "Pooled Synchronization Worked"), null);
}
catch (Exception ex)
{
Debug.WriteLine("Pooled SyncContext - Error occurred: {0}", ex);
}
}));

Gravatar

Hi Shawn,

This behavior is not different from an ordinary .NET app. The same NullReferenceException occurs. You need to cache the SynchronizationContent.Current while still on the UI thread before accessing that variable.

Something like this works fine;

var ctx = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(new WaitCallback(
o =>
{
try
{
ctx.Post(
new SendOrPostCallback(
ignore => results.Text = "Pooled Synchronization Worked"), null);
}
catch (Exception ex)
{
Debug.WriteLine("Pooled SyncContext - Error occurred: {0}", ex);
}
}));

Gravatar

I like using the Dispatcher as well over the SynchronizationContext. With the latter I have to catch it first before I can use it later while the former is just code and go. SynchronizationContext laves me the chance I might forget to catch it first, like your first example with it above.

Gravatar

I think I would be more comfortable with Dispatcher if it didn't feel like someone happened to find a refrence to the dispatcher in the Deployment object one day and decided to start using it. It just feels too undocumented and prone to future problems for something so central to Silverlight development. I have been using injection to get my Dispatcher, it sounds like I should be injecting SynchronizationContext instead.

Gravatar

Whatever works best with the fewest moving parts is OK with me. Thanks for the good post.

Gravatar

Looking at the internals of Dispatcher I am finding some interesting things. The Dispatcher's public Invoke methods always uses the Background priority level while DispatcherSynchronizationContext uses either the Normal or Send priority level and I am guessing that SynchronizationContext is going through the DispatcherSynchronizationContext. This makes me wonder if there are performance and/or stability differences between using the two methods caused by the difference in priority levels.

Also, it turns out that there actually is a Dispatcher.Current (really Dispatcher.MainDispatcher) that is a friend static property. The call to Deployment.Current.Dispatcher is really just a pass through to Dispatcher.MainDispatcher. Assuming there is a reason that MainDispatcher was not made public in the first place, what problems could we get into by getting to it through Deployment?

Gravatar

Colin,

I think the Deployment way is fine to use as its been suggested the best way to get to it (since other methods require access to UI-bound objects). Dunno why its different and the whole Dispatcher in SL is very different than on Windows.

Are you sure that its calling it under lower priority? Because my understanding in SL2 is that the dispatcher isn't really prioritized (unlike on WPF).

Gravatar

There are few good reasons why to use SynchronizationContext instead of Dispatcher. Creating multiplatform code (winforms/wpf/sl) is one of them, good design is the other. You can read more here http://mokosh.co.uk/post/Updating-ObservableCollection-from-background-thread.aspx

Gravatar

Greg,

I don't quite agree with your example as posting updates to the UI thread to update the OC is perfectly acceptable and using SynchronizationContext.Current == null means that you are *not* on the UI thread doesn't make sense to me either as if someone as added a SyncContext to the current thread (even if its not the UI thread), then you'll be updating from the non-UI thread.

Creating code that works well on all libraries (multi-platform usually means multiple O/S's) makes sense as a reason to use it, though if you're doing WPF/SL (no WinForms), the Dispatcher implementation works pretty well.

You state another reason to do it is "good design". Can you elaborate?

Gravatar

In your previous post "Should Silverlight 3's Out of Browser be Full-Trust" you wrote about how SL should stay in a sandbox and I totally agree with that. It means however, that code reuse is very importaint and need to to develop software for both SL and WPF (with more 'full-trusty' features in the later) is increasing.

This need drives development of projects such as Prism 2 which enables us to quite easly develop code for both WPF and SL. When I mentioned 'good design' I meant the design that takes platform independence into account. But of course meaning of 'good desing' differs slightly from project to project and is a matter of more context related judgement.

Gravatar

Oh, forgot about one other thing, threre's no Deployment.Current.Dispatcher in WPF, is threre?

Gravatar

Yes, I agree that Deployment.Dispatcher in WPF, but there is no Dispatcher.CurrentDispatcher in SL. SyncContext would solve that, *but* its one more thing to track IMHO. That means that you have to pass it into the background task and keep it around. I think that if the pattern were more like the way that .Current works in SyncContext, I'd use it everywhere...but the way it works today just makes it another piece of information to track. I'd like something in between. I am lobbying for the WPF syntax of Dispatcher in SL but no idea if it'll make it in.

Gravatar

Shawn,
Other than during disposal it looks like you are correct, only the Send priority gets any special treatment. Send gets pushed to the front of the queue bypassing anything else. Honestly, Dispatcher isn't coded very well if Reflector can be believed.

Gravatar

I think that title of your post may be little too strong (*don't use* part of it) as it is completely good thing to use SynchronizationContext.

Gravatar

Tom,

Yes, I agree (though I don't want to change the title because of links), but hopefully my UPDATE section tempers that a bit. When I wrote the article, I thought it was seriously bugged.

Gravatar

Nice blog...

Gravatar

Shawn,
Generally for SynchornizationContext to work,you should have a reference to the SynchronizationContext object at the Constructor level and then use the same to do ur post and sent.. It works perfectly that way :)

Gravatar

The SynchronizationContext class is a bit of a bugger to use directly. The standard approach is to use AsyncOperation/AsyncOperationManager, which wrap some of that complexity.

The MSDN article "Implementing the Event Based Asynchronous Pattern" has the best docs for this.


 



 
Save Cancel