What is SynchronizationContext, and what does it mean to capture and use it?

23 篇文章 0 订阅
22 篇文章 0 订阅

We in software development love abstractions.  We’re rarely happy hardcoding to a particular implementation; rather, when writing higher-level systems, we abstract away the details of a particular implementation so that we can plug in a different implementation later without having to change our higher-level system.  This is why we have interfaces, this is why we have abstract classes, this is why we have virtual methods, and so on.

SynchronizationContext is just an abstraction, one that represents a particular environment you want to do some work in.  As an example of such an environment, Windows Forms apps have a UI thread (while it’s possible for there to be multiple, for the purposes of this discussion it doesn’t matter), which is where any work that needs to use UI controls needs to happen.  For cases where you’re running code on a ThreadPool thread and you need to marshal work back to the UI so that this work can muck with UI controls, Windows Forms provides the Control.BeginInvoke method.  You give a delegate to a Control’s BeginInvoke method, and that delegate will be invoked back on the thread with which that control is associated.

So, if I’m writing a component that needs to schedule some work to the ThreadPool and then continue with some work back on the UI thread, I can code my component to use Control.BeginInvoke.  But now what if I decide I want to use my component in a WPF app? WPF has the same UI thread constraint that Windows Forms has, but it has a different mechanism for marshaling back to the UI thread: rather than using Control.BeginInvoke on a control associated with the right thread, you use Dispatcher.BeginInvoke (or InvokeAsync) on the Dispatcher instance associated with the right thread. 

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.

If I was writing my component specifically to target Windows Forms, I might implement my go-to-the-ThreadPool-and-then-back-to-the-UI-thread logic something like the following:

public static void DoWork(Control c)       
{        
    ThreadPool.QueueUserWorkItem(delegate        
    {        
        … // do work on ThreadPool        
        c.BeginInvoke(delegate        
        {        
            … // do work on UI        
        });        
    });        
}

If I was instead writing my component to use SynchronizationContext, I might instead write it as:

public static void DoWork(SynchronizationContext sc)       
{        
    ThreadPool.QueueUserWorkItem(delegate        
    {        
        … // do work on ThreadPool        
        sc.Post(delegate        
        {        
            … // do work on UI        
        }, null);        
    });        
}

Of course, it’s annoying (and prohibitive for certain desired programming models) to need to pass around the target context to come back to, so SynchronizationContext provides the Current property, which allows you to discover from the current thread the context that will let you get back to the current environment, if there is one.  This allows you to “capture it” (i.e. read the reference from SynchronizationContext.Current and store that reference for later usage):

public static void DoWork()       
{        
    var sc = SynchronizationContext.Current;        
    ThreadPool.QueueUserWorkItem(delegate        
    {        
        … // do work on ThreadPool        
        sc.Post(delegate        
        {        
            … // do work on the original context        
        }, null);        
   });        
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值