• 一文说通C#中的异步编程补遗


    前文写了关于C#中的异步编程。后台有无数人在讨论,很多人把异步和多线程混了。

    文章在这儿:一文说通C#中的异步编程

    所以,本文从体系的角度,再写一下这个异步编程。

    一、C#中的异步编程演变

    1. 异步编程模型

    这是C#中早期的异步模型,通过IAsyncResult接口来实现。

    实现的代码大体是这个样子:

    class MyClass
    {

        IAsyncResult BeginAction(para ..., AsyncCallback callback, object state);
        EndAction(IAsyncResult async_result);
    }

    这种方式在一些库里还有保留,像FileSteam类里的BeginReadEndRead方法组,就是这种方式。

    编程时,不建议用这种方式。

    2. 基于事件的异步模型

    这是C#中间一个过渡时期的异步模型,核心是基于一个或多个事件、事件处理委托的派生类型,是一种使用多线程的模式。

    这个模式在类库里,多用在Winform/WPF中的组件的事件处理,你可以随便拿一个Framework 4.5以前的组件去研究,大多数都是这种方式。

    这种方式的实现大体是这个样子:

    class MyClass
    {

      void ActionAsync(para ...);
      event ActionCompletedEventHandler action_completed;
    }

    这种方式使用多线程,所以,它具有多线程的全部特点和要求。

    从微软的建议来看,Framework 4.5以后,并不推荐使用这种模式。

    3. 基于任务的异步模型

    这种异步模型从Framework 4.0以后引入,使用单一方法来表示异步的开始和完成。这是目前推荐的异步开发方式。在上个文章中的异步模式,就是这个方式。

    这个方式的代码实现是这样的:

    class MyClass
    {

      Task<T> ActionAsync(para ...);
    }

    我们所说的异步,包括前文讲的异步,全部是基于这个基于任务的异步模型来讨论。

    在这个模型下,前文说过,异步不是多线程。今天再强调一遍,异步不仅不是多线程,同时异步也不一定会使用多线程。

        为了防止不提供原网址的转载,特在这里加上原文链接:https://www.cnblogs.com/tiger-wang/p/13428372.html

    二、异步模型中的“任务”

    先来看看任务:TaskTask<T>,这是异步模型的核心。

    这个“任务”,是一种“承诺”,承诺会在稍后完成任务。

    它有两个关键字:asyncawait。注意:是await,不是wait。这儿再强调一下,Task.Wait是个同步方法,用在多线程中等待。TaskThread的子集,因此继承了Wait方法,但这个方法不是给异步用的。

    在某些情况下,异步可以采用多线程来实现,这时候,Task.Wait可以用,但这是以多线程的身份来使用的,用出问题要查线程,而不是异步。

    关于异步中Taskasyncawait配合的部分,可以去看前一个文章。地址在:一文说通C#中的异步编程,这儿不再说了。

    三、异步编程的两种模式

    1. 单线程模式

    先看代码:

    Task<string> GetHtmlAsync()
    {
      var client = new HttpClient();
      var gettask = client.GetStringAsync("https://home.cnblogs.com/u/tiger-wang");

      return await gettask;
    }

    这种模式下,这个异步工作于单线程状态。代码虽然返回一个任务Task<T>,在这个任务依然在主线程中,并没有生成一个新的线程。换句话说,这种方式不额外占用线程池资源,也不需要考虑多线程开发中线程锁定、数据一致性等问题

    因为线程没有切换,所以也不存在上下文切换的问题

    2. 多线程模式

    既然Task派生自Thread,当然也可以用多线程来实现异步。

    看代码:

    Task<string> GetHtmlAsync()
    {
      var gettask = Task.Run(() => {
        var client = new HttpClient();
        return client.GetStringAsync("https://home.cnblogs.com/u/tiger-wang");
      });

      return await gettask;
    }

    对方上一段代码,把调用client.GetStringAsync的部分放到了Task.Run里。

    这种方式中,异步被放到了主线程以外的新线程中执行,换句话说,这个异步在以多线程的方式执行。

    在这种模式下,asyncawait的配合,以及对程序执行次序的控制,跟单线程模式是完全一样的。但是要注意,前边说了,asyncawait是异步的关键字,它不管多线程的事,也不会为多线程提供任何保护。多线程中的并发锁、数据锁、上下文切换,还需要以多线程的方式另外搞定。Task.Run的内部代码会占用线程池资源,并在一个可用的线程上与主线程并行运行。

    四、异步的两个额外状态

    1. 取消

    异步针对的是需要消耗长时间运行的工作。在工作过程中,如果需要,我们可以取消异步的运行。系统提供了一个类CancellationToken来处理这个工作。

    定义方式:

    Task<T> ActionAsync(para ..., CancellationToken cancellationtoken);

    调用方式:

    CancellationTokenSource source = new CancellationTokenSource();
    CancellationToken cancel_token = source.Token;

    await ActionAsync(para, cancel_token);

    需要取消时:

    source.Cancel();

    就可以了。

    在做API时,异步中加个CancellationToken,是基本的代码礼节。

    2. 进度

    长时间运行,如果能给出个进度也不错。

    定义方式:

    Task<T> ActionAsync(para ..., IProgress<T> progress);

    其中,T是需要返回的进度值,可以是各种需要的类型。

    当然,我们需要实现IProgress:

    public class Progress<T> : IProgress<T>  
    {  
        public Progress();  
        public Progress(Action<T> handler);  
        protected virtual void OnReport(T value);  
        public event EventHandler<T> ProgressChanged;  
    }  

    IProgress<T>通过回调来发送进度值,引发捕获并处理。

    全文完。

    这篇文章是对前一篇文章的补充和扩展。所以,要两篇一起看,才更好。

    一文说通C#中的异步编程


    微信公众号:老王Plus

    扫描二维码,关注个人公众号,可以第一时间得到最新的个人文章和内容推送

    本文版权归作者所有,转载请保留此声明和原文链接

  • 相关阅读:
    二维码跳转不同的 app store
    HTML 保存图片到本地
    HTML5 的一些小的整理吧
    Echarts 3.19 制作常用的图形 非静态
    word2010设置页码起始位置及前后页码不同的方法
    经struts2中的action后,jsp中css失效的问题
    在Linux和Windows下配置MyEclipse+Tomcat+JDK的一些问题
    MyEclipse自动退出问题解决办法
    (转)java web建议学习路线
    Linux中MyEclipse+tomcat遇到的问题
  • 原文地址:https://www.cnblogs.com/tiger-wang/p/13428372.html
Copyright © 2020-2023  润新知