前导

  Asynchronous programming Model(APM)异步编程模型以BeginMethod(...) 和 EndMethod(...)结对出现。
IAsyncResult  BeginGetResponse(AsyncCallback callback, object state)
WebResponse  EndGetResponse(IAsyncResult asyncResult)

   Event-based Asynchronous Pattern(EAP)模型以MethodAsync(...) 和CancelAsync(...) 结对出现,由Completed事件设置回调函数。

  .Net4.5开始Task Parallel Library(TPL) 为异步和并行编程提供新的模型,使异步和并发操作有统一的编程入口,

该模型常定义以Async后缀结尾的函数名、返回带有awaitable属性的Task/Task<T>对象, 如果你的program target设置为4.5+,可用Task-based Asynchronous Pattern (TAP)取代以上2种模型。

TAP

  TPL的核心是Task类,Task,Task<TResult>可以将其理解为一个包装委托(通常就是Action或Func委托)并执行的容器,有了Task几乎不用去和Thread 打交道,使用者只需要关注具体业务对应的Job,Task背后有一个 TaskScheduler 的类来负责调度Task的执行,这样Task对象将在默认的TaskScheduler调度下执行,TaskScheduler使用线程池中的线程,至于是新建还是使用已有线程这个对用户是完全透明的,也可以通过重载函数的参数传入自定义的TaskScheduler。

Task任务状态:

明确Task和线程的关系:

  • 任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行
  • 任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,在.NET面向任务异步编程模型中,你只需要关注业务概念的任务,具备底层实现会由Task包装完成。
Task相比ThreadPool的优势:
  • ThreadPool不支持线程取消、完成、失败通知等交互新操作
  • ThreadPool不支持线程执行的先后顺序。

 

await/async 语法糖

  TAP异步编程模型的核心是塑造异步操作的Task、Task<T>对象,这是awaitable 对象,await/async语法糖简化了写法

  • 对于I/O-bound 代码,编写一个返回Task或Task<T>的async方法, 之后await 这个方法

  • 对于CPU-bound代码,使用Task.Run方法后台启动一个操作,之后await这个操作。

  魔法发生在await关键字,会将控制权上交给执行Async方法的上层调用者。

  在C#语言底层,编译器将你的await/async 代码转换为状态机, 记录了当await发生时控制权上交后台工作完成时恢复执行的标记。

 
  异步编程在实践时需要理解:
  • 异步代码可用于I/O -bound 和CPU-bound 代码, 但是2个场景的写法是不同的

  • 异步编程利用Task和Task<T>对象来 塑造需要在后台完成的工作

  • async关键字将方法转变为异步方法,这样可在方法体使用await关键词, 如果async方法内不包含await关键词,那将不会上交控制权

  • 当await动作发生时,将会暂停(注意是suspend 而不是block)方法,并将控制权上交给调用者(直到awaitable任务完成)

  • await 只能被用在async方法内部

执行操作的“异步方式”


下面是一个I/O-bound的例子:    

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
namespace Test
{
    class Program
    {
        static void Main(string[] args)
       {
         var asyncMethod =  AccessTheWebAsync();
         Console.WriteLine("go on ...... "+ Thread.CurrentThread.ManagedThreadId );
         // 等待异步线程处理完毕,没有以下句子,await使控制回到调用方,主线程即终止。
         asyncMethod.Wait();
       }
      public static async Task<int> AccessTheWebAsync()
      {
          HttpClient client = new HttpClient();
          // GetStringAsync returns a Task<string>.
          // That means that when you await the task you'll get a string (urlContents).
          Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
          // You can do work here that doesn't rely on the string from GetStringAsync.
         DoIndependentWork();

        // The await operator suspends AccessTheWebAsync.
        // - AccessTheWebAsync can't continue until getStringTask is complete.
        // - Meanwhile, control returns to the caller of AccessTheWebAsync.
        // - Control resumes here when getStringTask is complete.
        // - The await operator then retrieves the string result from getStringTask.

         string urlContents = await getStringTask;
         Console.WriteLine(urlContents.Length+"....... "+Thread.CurrentThread.ManagedThreadId );
         // The return statement specifies an integer result.
         // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
         return urlContents.Length;
       }
        public static void DoIndependentWork()
        {
            Console.WriteLine("work ......"+Thread.CurrentThread.ManagedThreadId);
        }
    }
}

【.NET异步编程系列1】:await&amp;async语法糖让异步编程如鱼得水-LMLPHP

  以上代码在ASP.NET 或GUI程序可能会发生死锁, 具体参见《.NET异步编程系列3:掌握SynchronizationContext避免deadlock》;控制台程序经过验证在.NET Core 和.Net Framework上都没有SynchronizationContext,故不会发生死锁。

Task对象提供了丰富的API帮助我们完成 基于任务的异步操作, 让我们专注业务概念的任务

作者:Julian_酱

感谢您的认真阅读,如有问题请大胆斧正,如果您觉得本文对你有用,不妨右下角点个【.NET异步编程系列1】:await&amp;async语法糖让异步编程如鱼得水-LMLPHP或加关注。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置注明本文的作者及原文链接,否则保留追究法律责任的权利。

04-11 11:01