c#同步上下文SynchronizationContext的作用(一)

同步上下文的作用,官方解释是:

提供在各种同步模型中传播同步上下文的基本功能。同步上下文的工作就是确保调用在正确的线程上执行。


官方解释抽象难以理解,摘抄了网上了其它的解释如下:

在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();
    }
}

来张方法的缩略图:

image.png


使用上下文的这些方法,最基本的应用就是用来跨线程更新UI。

WinFroms和WPF都继承了SynchronizationContext,使同步上下文能够在UI线程或者Dispatcher线程上正确执行

System.Windows.Forms. WindowsFormsSynchronizationContext
System.Windows.Threading. DispatcherSynchronizationContext




勇哥写了段演示代码,有详细注释:


其中第三项inoke,是我们之前很熟悉的异步调用。

最后一项Task的,是用到.net 4.0的Task的TaskScheduler(任务调度),属于新特性。

image.png


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啥子不同的问答篇,涨一下知识。


image.png


--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年5月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864