zoukankan      html  css  js  c++  java
  • 一文说通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

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

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

  • 相关阅读:
    10. Regular Expression Matching
    9. Palindrome Number
    6. ZigZag Conversion
    5. Longest Palindromic Substring
    4. Median of Two Sorted Arrays
    3. Longest Substring Without Repeating Characters
    2. Add Two Numbers
    链式表的按序号查找
    可持久化线段树——区间更新hdu4348
    主席树——树链上第k大spoj COT
  • 原文地址:https://www.cnblogs.com/tiger-wang/p/13428372.html
Copyright © 2011-2022 走看看