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

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

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

接上篇 


上篇写的演示程序中关于SynchronizationContext的post方法的示例,并没有实际意义。

再来一个实例:


程序三个按钮,我们想按下按钮后访问网页,把其内容赋值给按钮的Text属性

image.png


第一按钮借助TaskScheduler的方法FromCurrentSynchronizationContext

该方法会创建一个SynchronizationContextTaskScheduler实例并返回,以便在原始的SynchronizationContext.Current上的Post方法对任务进行排队执行。

注意Debug.WriteLine(1) 和 (2) 输出两个调试信息, 显示托管的线程ID, 你会发现UI线程ID、异步代码中的线程ID是相同的。证明了异常代码被妥托到了UI线程上执行了。


第二个铵钮只接用SynchronizationContext的Current属性。它用于获取当前线程的同步上下文。

C#
如果我正在编写一个库,想要停下来做一些工作,然后将委托排队送回“原始上下文”继续执行,
那么我只需要获取他们的SynchronizationContext,存下来。
当完成工作后,在该上下文上调用Post去传递我想要调用的委托即可。
我不需在WinForm中知道要获取一个控件并调用BeginInvoke,
不需要在WPF中知道要对Dispatcher进行BeginInvoke,
也不需要在xunit中知道要以某种方式获取其上下文并排队,
我只需要获取当前的SynchronizationContext并在以后使用它就可以了。
为此,借助SynchronizationContext提供的Current属性


第三个按钮使用.net4.0的 async/await的异步机制实现了上面两个按钮的功能。

这是一种非常自然的写法,把异步代码写成了同步代码的形式。

C#
就这样,成功在UI线程上设置了按钮的内容,与上面两个按钮实现的手动版本一样,await Task默认会关注SynchronizationContext.Current和TaskScheduler.Current两个参数。
当你在C#中使用await时,编译器会进行代码转换来向“可等待者”(这里为Task)
索要(通过调用GetAwaiter)“awaiter”(这里为TaskAwaiter<string>)。
该awaiter负责挂接回调(通常称为“继续(continuation)”),
当等待的对象完成时,该回调将被封送到状态机,
并使用在注册回调时捕获的上下文或调度程序来执行此回调。
尽管与实际代码不完全相同(实际代码还进行了其他优化和调整),
但大体上是这样的:object scheduler = SynchronizationContext.Current;if (scheduler is null && TaskScheduler.Current != TaskScheduler.Default){
    scheduler = TaskScheduler.Current;}说人话就是,它先检查有没有设置当前SynchronizationContext,如果没有,
则再判断当前调度程序是否为默认的TaskScheduler。
如果不是,那么当准备好调用回调时,会使用该调度程序执行回调;
否则,通常会作为完成已等待任务的操作的一部分来执行回调
(译注:这个“否则”我也没看懂,我的理解是如果有当前上下文,
则使用当前上下文执行回调;如果当前上下文为空,
且使用的是默认调度程序ThreadPoolTaskScheduler,则会启用线程池线程执行回调)。



代码:

C#
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Diagnostics;using System.Drawing;using System.Linq;using System.Net.Http;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;namespace WindowsFormsApplication7{
    public partial class Form1 : Form    {
        private static readonly HttpClient s_httpClient = new HttpClient();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            /*
            假设有一个UI App,它有一个按钮。当点击按钮后,会从网上下载一些文本并将其设置为按钮的内容。
            我们应当只在UI线程中访问该按钮,因此当我们成功下载新的文本后,
            我们需要从拥有按钮控制权的的线程中将其设置为按钮的内容。
            */
            Debug.WriteLine($"(1){Environment.CurrentManagedThreadId}");
            s_httpClient.GetStringAsync("https://www.baidu.com/").ContinueWith(downloadTask =>
            {
                downloadBtn.Text = downloadTask.Result;
                Debug.WriteLine($"(2){Environment.CurrentManagedThreadId}");
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            /*
            或直接使用SynchronizationContext:
            不过,这两种方式都需要显式指定回调,更好的方式是通过async/await自然地进行编码, 
            见第三个按钮的代码
            */
            Debug.WriteLine($"(1){Environment.CurrentManagedThreadId}");
            SynchronizationContext sc = SynchronizationContext.Current;
            s_httpClient.GetStringAsync("https://www.baidu.com/").ContinueWith(downloadTask =>
            {
                sc.Post(delegate
                {
                    downloadBtn2.Text = downloadTask.Result;
                    Debug.WriteLine($"(2){Environment.CurrentManagedThreadId}");
                }, null);
            });
        }

        private async void downloadBtn3_Click(object sender, EventArgs e)
        {
            string text = await s_httpClient.GetStringAsync("https://www.baidu.com/");
            downloadBtn3.Text = text;
        }
    }}


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

作者:hackpig

来源:www.skcircle.com

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

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

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

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

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

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

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

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

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

C#中使用RegisterWindowMessage进行进程之间进行通讯

C#中使用RegisterWindowMessage进行进程之间进行通讯

注:大家都知道在window里,进程之间通讯可以用消息通讯。但是有个较冷门的知识就是RegisterWindowMessage。一般的sendmessage和postmessage是需要在窗体程序间进行通讯,你得知道对方窗口的句柄。这个句柄每次启动程序时是系统分配的,每次不一样。有了这个Regist...

C#:多进程开发,控制进程数量

C#:多进程开发,控制进程数量

正在c#程序优化时,如果多线程效果不佳的情况下,也会使用多进程的方案,如下:C#System.Threading.Tasks.Task task=System.Threading.Tasks.Task.Factory.StartNew(     &...

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

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

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

细说进程、应用程序域与上下文之间的关系(二)—— 应用程序域

细说进程、应用程序域与上下文之间的关系(二)—— 应用程序域

目录一、进程的概念与作用二、应用程序域三、深入了解.NET上下文四、进程应用程序域与线程的关系 二、应用程序域使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中。应用程序域是.NET引入的一个新概念,它比进程所占用的资源要少,...

细说进程、应用程序域与上下文之间的关系(一)——进程的概念与作用

细说进程、应用程序域与上下文之间的关系(一)——进程的概念与作用

引言本文主要是介绍进程(Process)、应用程序域(AppDomain)、.NET上下文(Context)的概念与操作。虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高系统的性能有莫大的帮助。在本篇最后的一节当中将会介绍到三者与线程之间的关系,希望对多线程开发人员能提供...

发表评论

访客

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