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

接上篇 


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

再来一个实例:


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

image.png


第一按钮借助TaskScheduler的方法FromCurrentSynchronizationContext

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

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


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

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


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

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

就这样,成功在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,则会启用线程池线程执行回调)。



代码:

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

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



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

发表评论:

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

会员中心
搜索
«    2024年3月    »
123
45678910
11121314151617
18192021222324
25262728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 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