同步上下文的作用,官方解释是:
提供在各种同步模型中传播同步上下文的基本功能。同步上下文的工作就是确保调用在正确的线程上执行。
官方解释抽象难以理解,摘抄了网上了其它的解释如下:
在99.9%的使用场景中,SynchronizationContext仅仅被当作一个提供虚(virtual)Post方法的类, 该方法可以接收一个委托,然后异步执行它。虽 然SynchronizationContext还有许多其他的虚成员, 但是很少使用它们,而且和我们今天的内容无关,就不说了。 Post方法的基础实现就仅仅是调用一下ThreadPool.QueueUserWorkItem, 将接收的委托加入线程池队列去异步执行。 另外,派生类可以选择重写(override)Post方法,让委托在更加合适的位置和时间去执行。 例如,WinForm有一个派生自SynchronizationContext的类,重写了Post方法, 内部执行Control.BeginInvoke, 这样,调用该Post方法就会在该控件的UI线程上执行接收的委托。 WinForm依赖Win32的消息处理机制,并在UI线程上运行“消息循环”, 该线程就是简单的等待新消息到达,然后去处理。 这些消息可能是鼠标移动和点击、键盘输入、系统事件、可供调用的委托等。 所以,只需要将委托传递给SynchronizationContext实例的Post方法,就可以在控件的UI线程中执行。 和WinForm一样,WPF也有一个派生自SynchronizationContext的类,重写了Post方法, 通过Dispatcher.BeginInvoke将接收的委托封送到UI线程。 与WinForm通过控件管理不同的是,WPF是由Dispatcher管理的。 Windows运行时(WinRT)也不例外,它有一个派生自SynchronizationContext的类, 重写了Post方法,通过CoreDispatcher将接收的委托排队送到UI线程。
与抽象的优点一样:SynchronizationContext它提供了一个API, 可用于将委托排队进行处理,无需了解该实现的细节,这是实现者所期望的。 所以,如果我正在编写一个库,想要停下来做一些工作,然后将委托排队送回“原始上下文”继续执行, 那么我只需要获取他们的SynchronizationContext,存下来。 当完成工作后,在该上下文上调用Post去传递我想要调用的委托即可。 我不需在WinForm中知道要获取一个控件并调用BeginInvoke, 不需要在WPF中知道要对Dispatcher进行BeginInvoke, 也不需要在xunit中知道要以某种方式获取其上下文并排队, 我只需要获取当前的SynchronizationContext并在以后使用它就可以了。
SynchronizationContext类的方法原型如下:
namespace System.Threading { // // 摘要: // 提供在各种同步模型中传播同步上下文的基本功能。 public class SynchronizationContext { // // 摘要: // 创建 System.Threading.SynchronizationContext 类的新实例。 public SynchronizationContext(); // // 摘要: // 获取当前线程的同步上下文。 // // 返回结果: // 一个 System.Threading.SynchronizationContext 对象,它表示当前同步上下文。 public static SynchronizationContext Current { get; } // // 摘要: // 设置当前同步上下文。 // // 参数: // syncContext: // 要设置的 System.Threading.SynchronizationContext 对象。 [SecurityCritical] [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public static void SetSynchronizationContext(SynchronizationContext syncContext); // // 摘要: // 用于等待指定数组中的任一元素或所有元素接收信号的 Helper 函数。 // // 参数: // waitHandles: // 一个类型为 System.IntPtr 的数组,其中包含本机操作系统句柄。 // // waitAll: // 若等待所有句柄,则为 true;若等待任一句柄,则为 false。 // // millisecondsTimeout: // 等待的毫秒数,或为 System.Threading.Timeout.Infinite (-1),表示无限期等待。 // // 返回结果: // 满足等待的对象的数组索引。 [CLSCompliant(false)] [PrePrepareMethod] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] [SecurityCritical] protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout); // // 摘要: // 当在派生类中重写时,创建同步上下文的一个副本。 // // 返回结果: // 一个新的 System.Threading.SynchronizationContext 对象。 public virtual SynchronizationContext CreateCopy(); // // 摘要: // 确定是否需要等待通知。 // // 返回结果: // 如果需要等待通知,则为 true;否则为 false。 public bool IsWaitNotificationRequired(); // // 摘要: // 当在派生类中重写时,响应操作已完成的通知。 public virtual void OperationCompleted(); // // 摘要: // 当在派生类中重写时,响应操作已开始的通知。 public virtual void OperationStarted(); // // 摘要: // 当在派生类中重写时,将异步消息调度到一个同步上下文。 // // 参数: // d: // 要调用的 System.Threading.SendOrPostCallback 委托。 // // state: // 传递给委托的对象。 public virtual void Post(SendOrPostCallback d, object state); // // 摘要: // 当在派生类中重写时,将一个同步消息调度到一个同步上下文。 // // 参数: // d: // 要调用的 System.Threading.SendOrPostCallback 委托。 // // state: // 传递给委托的对象。 // // 异常: // T:System.NotSupportedException: // 在 Windows Store 应用程序中调用的方法。用于 Windows Store 应用程序的 System.Threading.SynchronizationContext // 的实现应用不支持 System.Threading.SynchronizationContext.Send(System.Threading.SendOrPostCallback,System.Object) // 方法。 public virtual void Send(SendOrPostCallback d, object state); // // 摘要: // 等待指定数组中的任一元素或所有元素接收信号。 // // 参数: // waitHandles: // 一个类型为 System.IntPtr 的数组,其中包含本机操作系统句柄。 // // waitAll: // 若等待所有句柄,则为 true;若等待任一句柄,则为 false。 // // millisecondsTimeout: // 等待的毫秒数,或为 System.Threading.Timeout.Infinite (-1),表示无限期等待。 // // 返回结果: // 满足等待的对象的数组索引。 // // 异常: // T:System.ArgumentNullException: // waitHandles 为 null。 [CLSCompliant(false)] [PrePrepareMethod] [SecurityCritical] public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout); // // 摘要: // 设置指示需要等待通知的通知,并准备回调方法以使其在发生等待时可以更可靠地被调用。 [SecuritySafeCritical] protected void SetWaitNotificationRequired(); } }
来张方法的缩略图:
使用上下文的这些方法,最基本的应用就是用来跨线程更新UI。
WinFroms和WPF都继承了SynchronizationContext
,使同步上下文能够在UI线程或者Dispatcher
线程上正确执行
System.Windows.Forms. WindowsFormsSynchronizationContext System.Windows.Threading. DispatcherSynchronizationContext
勇哥写了段演示代码,有详细注释:
其中第三项inoke,是我们之前很熟悉的异步调用。
最后一项Task的,是用到.net 4.0的Task的TaskScheduler(任务调度),属于新特性。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication6 { public partial class Form1 : Form { //提供在各种同步模型中传播同步上下文的基本功能。同步上下文的工作就是确保调用在正确的线程上执行。 //Current 获取当前同步上下文 public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { var context = SynchronizationContext.Current; SendOrPostCallback callback = o => { //TODO: label1.Text = "Hello Send"; }; //Send 一个同步消息调度到一个同步上下文。 //send调用后会阻塞直到调用完成。 context.Send(callback, null); } private void button2_Click(object sender, EventArgs e) { var context = SynchronizationContext.Current; SendOrPostCallback callback = o => { //TODO: label2.Text = "Hello Post"; }; //Post 将异步消息调度到一个同步上下文。 //和send的调用方法一样,不过Post会启动一个线程来调用,不会阻塞当前线程。 context.Post(callback, null); } private void button3_Click(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(BackgroudRun); } private void BackgroudRun(object state) { this.Invoke(new Action(() => { label3.Text = "Hello Invoke"; })); } private void button4_Click(object sender, EventArgs e) { var context = SynchronizationContext.Current; //获取同步上下文 Debug.Assert(context != null); ThreadPool.QueueUserWorkItem(BackgroudRun2, context); } private void BackgroudRun2(object state) { var context = state as SynchronizationContext; //传入的同步上下文 Debug.Assert(context != null); SendOrPostCallback callback = o => { label4.Text = "Hello SynchronizationContext"; }; context.Send(callback, null); //调用 } private void button5_Click(object sender, EventArgs e) { // 创建一个SynchronizationContext 关联的 TaskScheduler var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => label5.Text = "Hello TaskScheduler", CancellationToken.None, TaskCreationOptions.None, scheduler); } } }
另载一篇关于Application.Current.Dispatcher跟SynchronizationContext啥子不同的问答篇,涨一下知识。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

