当前位置:首页 > C#编程 > C#/.net框架 > 正文内容

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

Jorge2年前 (2022-05-09)C#/.net框架771

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

C#

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


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

C#
在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线程。
C#
与抽象的优点一样:SynchronizationContext它提供了一个API,
可用于将委托排队进行处理,无需了解该实现的细节,这是实现者所期望的。
所以,如果我正在编写一个库,想要停下来做一些工作,然后将委托排队送回“原始上下文”继续执行,
那么我只需要获取他们的SynchronizationContext,存下来。
当完成工作后,在该上下文上调用Post去传递我想要调用的委托即可。
我不需在WinForm中知道要获取一个控件并调用BeginInvoke,
不需要在WPF中知道要对Dispatcher进行BeginInvoke,
也不需要在xunit中知道要以某种方式获取其上下文并排队,
我只需要获取当前的SynchronizationContext并在以后使用它就可以了。



SynchronizationContext类的方法原型如下:

C#
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线程上正确执行

C#
System.Windows.Forms. WindowsFormsSynchronizationContext
System.Windows.Threading. DispatcherSynchronizationContext




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


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

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

image.png


C#
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

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

扫描二维码推送至手机访问。

版权声明:本文由7点博客发布,如需转载请注明出处。

本文链接:http://6dot.cn/?id=87

标签: .NET.NET框架
分享给朋友:

“c#同步上下文SynchronizationContext的作用(一)” 的相关文章

C#的propertygrid控件,选择和修改项目时很慢

C#的propertygrid控件,选择和修改项目时很慢

C#的propertygrid控件是很强。可以实现类似Vitual Studio属性面板那样的效果。但是。。。。我们一直痛苦它在选择和修改项目的时候很慢。我的用法是使用控件的SelectObject来绑定数据。C# PgridMotionSpeed.SelectedObject ...

C# Modelbus crc16计算校验和程序

C# Modelbus crc16计算校验和程序

我们手里一个无刷电机,采用485的modelbus crc16协议来控制。因此需要一个计算校验和的工具。源码:C#using System;using System.Collections.Generic;using System.ComponentModel;usin...

C# tableLayoutPanel动态加载控件闪烁的解决方案

C# tableLayoutPanel动态加载控件闪烁的解决方案

WinForm加载多个自定义控件时,会出现很严重的闪烁问题,很卡,一块一块的加载(像打开网页时,网络很卡的那种感觉)简直没法忍受。在网上搜索了好久,网上大部分的方法是一下4种,但是都不能有效的解决问题。  1.将DoubleBuffered 设置 true,用双缓存处理Form界面内容加载,可以提高...

计算代码执行时间,可以精确到十亿分之一秒

计算代码执行时间,可以精确到十亿分之一秒

注:.Net的Stopwatch类可以精确到1/10000毫秒, 有没有更精确的计时吗?见下面的代码。暂时没试过效果,大家可以试下。计算某个代码片段的执行时间,精确到CPU执行一条指令所用的时间(十亿分之一秒),可用于精确计算某个算法的执行时间。 代码:C#using Syste...

索引器(C# 编程指南)

索引器(C# 编程指南)

 索引器允许类或结构的实例就像数组一样进行索引。 无需显式指定类型或实例成员,即可设置或检索索引值。 索引器类似于属性,不同之处在于它们的访问器需要使用参数。以下示例定义了一个泛型类,其中包含用于赋值和检索值的简单 get 和 set 访问器方法。&...

关于C#项目引用的一点经验

关于C#项目引用的一点经验

关于项目引用,有几种:(一)这种是引用系统的程序集(二)下面这种是引用你自己的项目“解决方案”(三)最后一种是浏览本机上的项目的dll。对于工程中有几十个项目的软件来说,虽然使用(二)是很方便。但是会编译速度奇慢,而且随着项目越多越慢。貌似他run之前都会把所有项目都试图更新一下。勇哥宿舍的电脑,实...

发表评论

访客

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