Thursday, September 11, 2014

An Advantage of Using Current SynchronizationContext over Control.BeginInvoke

SynchronizationContext vs Control.BeginInvoke


Windows Forms and WPF framework do not allow modifications to UI elements and controls from a non-UI thread. So if a task thread wants to display a result on the UI or change a control to indicate state of some sort it has to somehow communicate with the UI thread and the UI thread should take care of displaying the UI updates, carry out the desired change to UI controls, etc.

There are two to accomplish this:

  • Capturing the SynchronizationContext as in the code snippet below which is assumed to part of a method in child class of the System.Windows.Form class:
      Task.Factory.StartNew(() =>
      {
        // update UI here
      }, this.cancelToken.Token
       , TaskCreationOptions.None
       , TaskScheduler.FromCurrentSynchronizationContext()
     );
    

  • Using Control.BeginInvok as in the code snippet below which also assumes to be with a child class of the the System.Windows.Form class:
    Task.Factory.StartNew(() =>
    {
        DoSomeWrok();
        Action updateUIDelegate = () =>{ UpdateUI();}; 
        /* ^^ a way of creating a delegate to be passed to BeginInvoke */
        this.BeginInvoke(updateUIDelegate); //will be execute on the UI thread.
        /* ^ this refers to the Form instance */
    }, this.cancelToken.Token
    );
    

    Notice in the above code we did not need to capture the current SynchronizationContext.

One Reason to Prefer Capturing Current SynchronizationContext:


Control.BeginInvok is not available in WPF. So if you are creating classes to be used in both Windows Forms and WPF then using SynchronizationContext is the portable way. Here is more from http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx:
We now have two different APIs for achieving the same basic operation, so how do I write my component to be agnostic of the UI framework?  By using SynchronizationContext.  SynchronizationContext provides a virtual Post method; this method simply takes a delegate and runs it wherever, whenever, and however the SynchronizationContext implementation deems fit.  Windows Forms provides the WindowsFormSynchronizationContext type which overrides Post to call Control.BeginInvoke.  WPF provides the DispatcherSynchronizationContext type which overrides Post to call Dispatcher.BeginInvoke.  And so on.  As such, I can now code my component to use SynchronizationContext instead of tying it to a specific framework.
I was curios how WindowsFormsSynchronizationContext mentioned in the article mentioned above implemented the Post method so I took a look at the code (reflected):


public override void Post(SendOrPostCallback d, object state)
{
 if (this.controlToSendTo != null)
 {
  this.controlToSendTo.BeginInvoke(d, new object[]
  {
   state
  });
 }
}

No comments:

Post a Comment