C# 异步编程(async和await)

 

1. 源起

在我们从 .Net 转 .Net Core 的过程中,我们通常会查看一些源码或者看书进行学习,在其中你可能会看到类似于这样的代码

 1 // 1. Asp.Net Core 的 控制器 类
 2 [HttpGet]
 3 public async Task<IActionResult> Index()
 4 {
 5      ......
 6      await ......
 7      ......
 8 }
 9 
10 // 2. Console 的 Program.cs 文件
11 static async Task Main(string[] args)
12 {
13      ......
14      await ......
15      ......
16 }

这是一个很重要的 “新特性”---异步编程。在 C# 里面异步编程是通过关键字 async 和 await 两个关键字实现的。

那什么是异步编程?async、await是怎么样实现异步编程的呢?

2. 为什么要异步编程

在介绍异步编程之前,我们先说一下同步编程。

通常我们编写的代码在被执行时,通常是自上而下一行一行的执行,如过是执行方法,则进入方法内部继续一行一行执行。

这就是同步执行,也称之为同步编程(也可以说是编程)。

你会发现同步编程有一个问题,就是但是当一个方法执行耗时比较长时(例如读取文件内容),会阻塞下面的代码执行,整个软件都会进入等待状态(如是GUI界面,则执行操作的线程会阻塞,整个操作界面也会处于等待状态),体验度相当的不好---尤其是在计算机有多核的情况下,完全可以在另一个CPU上干其他的工作,同时计算机完成耗时任务的时候通知你。这就是异步编程的起源更多示例

3. C# 异步编程

通过使用异步编程,你可以避免性能瓶颈并增强应用程序的总体响应能力。 但是,编写异步应用程序的传统技术可能比较复杂,使它们难以编写、调试和维护。

虽然 .Net 中有几种异步编程模式,但是目前建议使用的只有一种(TAP),我们将对历史有一些简单的说明,对于EAP进行更深入的介绍。

3.1 .Net 提供了3中异步编程模型(更多介绍

  • TAP(基于任务的异步模式):使用单一方法表示异步操作的开始和结束,是.Net中进行异步编程的推荐方法。(.Net Framework 4中引入的)
  • EAP(基于事件的异步模式):提供异步行为的基于事件的旧模型。 这种模式需要后缀为 Async 的方法,以及一个或多个事件、事件处理程序委托类型和 EventArg 派生类型。 EAP 是在 .NET Framework 2.0 中引入的。 不建议新的开发使用此模式。
  • APM(异步编程模型):(也称为 IAsyncResult 模式),这是使用 IAsyncResult 接口提供异步行为的旧模型。 在这种模式下,同步操作需要 Begin 和 End 方法(例如,BeginWrite 和 EndWrite以实现异步写入操作)。 不建议新的开发使用此模式。

3.2 TAP(基于任务的异步模式)

C# 拥有语言级别的异步编程模型,它遵循基于任务的异步模式。

C# 异步编程的核心是 Task 和 Task<T> 对象(深入了解Task和Task<T>),这两个对象对异步操作建模。它们受关键字 async 和 await 的支持(二者是异步编程的核心关键字)。通过这两个关键字,可以使用 .NET Framework、.NET Core 或 Windows 运行时中的资源,轻松创建异步方法(几乎与创建同步方法一样轻松)。 使用 async 关键字定义的异步方法简称为“异步方法”。await 关键字控制执行 await 的方法的调用方,且它最终允许 UI 具有响应性或服务具有灵活性。

4. 异步编程 示例

 

  1 using System;
  2 using System.Threading;
  3 using System.Threading.Tasks;
  4 
  5 namespace ConsoleApp
  6 {
  7     class Program
  8     {
  9         static async Task Main(string[] args)
 10         {
 11             try
 12             {
 13                 //线程池,最大和最小数量(CompletionPortThreads 异步I/O线程数量)
 14                 //相关资料:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/the-managed-thread-pool
 15                 ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);       //6 6
 16                 ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);       //32767 1000
 17 
 18                 ShowMsg(minWorkerThreads.ToString());
 19                 ShowMsg(minCompletionPortThreads.ToString());
 20                 ShowMsg(maxWorkerThreads.ToString());
 21                 ShowMsg(maxCompletionPortThreads.ToString());
 22 
 23                 #region 【1. 使用示例】
 24                 ////测试async和await(运行在ThreadPool)
 25                 ////相关资料:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model
 26                 //ShowMsg($"【Main start 1】");
 27                 //Test1Async();        //异步执行,但是不知道什么时候执行
 28                 //Test11Async();       //异步执行,但是不知道什么时候执行
 29                 //ShowMsg($"【Main end 1】");
 30                 //ShowMsg($"【Main start 2】");
 31                 //await Test2Async();  //同步执行,等待任务执行完成
 32                 //ShowMsg($"【Main end 2】");
 33 
 34                 //ShowMsg($"【Main start 3】");
 35                 //Task<String> task = Test3Async();    //有返回值的方法,异步执行,但是不知道什么时候执行
 36                 //ShowMsg($"【Main end Task】");
 37                 //ShowMsg($"{task.Result}");        //等待异步操作执行完成之后,获取执行结果(相当于await)
 38 
 39                 //ShowMsg($"【Main end 3】");
 40 
 41                 //TaskRun();
 42 
 43                 //ShowMsg($"【Used samples end】");
 44 
 45                 #endregion
 46 
 47                 #region 【2. 错误捕获示例】
 48                 ////相关资料:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/try-catch#async-method-example
 49                 //// sample 1
 50                 //await Exception1Async();
 51 
 52                 //// sample 2
 53                 //Task<String> exceptionSample1 = Exception1Async();
 54                 //exceptionSample1.Wait();
 55 
 56                 //// sample 3
 57                 //Console.WriteLine(exceptionSample1.Result);
 58 
 59                 //// sample 4
 60                 //Exception1Async();
 61 
 62                 // 结论:
 63                 //     以上四种示例,前三种会进入外层try-catch,第四种则不会(因为是异步执行,不需要等待回传结果,需要自行处理报错信息)
 64 
 65                 //if (null != exceptionSample1.Exception)
 66                 //{
 67                 //}
 68                 #endregion
 69 
 70             }
 71             catch (Exception ex)
 72             {
 73                 Console.WriteLine(ex);
 74             }
 75 
 76             ShowMsg("The End!");
 77         }
 78 
 79         static async Task Test1Async()
 80         {
 81             ShowMsg($"Test start 1");
 82             await Task.Delay(300);
 83             ShowMsg($"Test end 1");
 84         }
 85 
 86         static async Task Test11Async()
 87         {
 88             ShowMsg($"Test start 1111");
 89             await Task.Delay(300);
 90             ShowMsg($"Test end 1111");
 91         }
 92 
 93         static async Task Test2Async()
 94         {
 95             ShowMsg($"Test start 2");
 96             await Task.Delay(300);
 97             ShowMsg($"Test end 2");
 98         }
 99 
100         static async Task<String> Test3Async()
101         {
102             ShowMsg($"Test start 3");
103             await Task.Delay(300);
104             ShowMsg($"Test end 3");
105             return "123";
106         }
107 
108         static void ShowMsg(String msg)
109         {
110             Console.WriteLine($" Time:{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffff") } . Message: {msg}");
111         }
112 
113         static async Task<String> Exception1Async()
114         {
115             await Task.Delay(100);
116 
117             // Uncomment each of the following lines to
118             // demonstrate exception handling.
119 
120             throw new OperationCanceledException("canceled");
121             throw new Exception("Something happened.");
122             return "Done";
123         }
124 
125         static async Task TaskRun()
126         {
127             await Task.Run(() =>
128             {
129                 Thread.Sleep(100);
130                 ShowMsg("This is task run method.");
131             });
132         }
133 
134     }
135 }

 

 

 

附录

异步编程

https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Concepts

 

异步编程模式

https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/


C# 异步编程
https://docs.microsoft.com/zh-cn/dotnet/csharp/async

使用 Async 和 Await 的异步编程
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/

异步编程模型
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model

try-catch(C# 参考)
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/try-catch

托管线程池
https://docs.microsoft.com/zh-cn/dotnet/standard/threading/the-managed-thread-pool

深入了解异步
https://docs.microsoft.com/zh-cn/dotnet/standard/async-in-depth

基于任务的异步模式
https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap

 

任务并行库

https://docs.microsoft.com/zh-cn/dotnet/standard/parallel-programming/task-parallel-library-tpl

posted @ 2021-07-21 17:02  暗香残留  阅读(1160)  评论(0编辑  收藏  举报