[C#] 异步编程 - 剖析异步方法

剖析异步方法

  这是上篇《开始接触 async/await 异步编程》(入门)的第二章内容,主要是深入了解异步方法,建议大家先看入门篇,很短。

  本文要求了解委托的使用。

目录

介绍异步方法

异步方法:在执行完成前立即返回调用方法,在调用方法继续执行的过程中完成任务。

语法分析:

(1)关键字:方法头使用 async 修饰。

(2)要求:包含 N(N>0) 个 await 表达式,表示需要异步执行的任务。

(3)返回类型:只能返回 3 种类型(void、Task 和 Task<T>)。Task 和 Task<T> 标识返回的对象会在将来完成工作,表示调用方法和异步方法可以继续执行。

(4)参数:数量不限。但不能使用 out 和 ref 关键字。

(5)命名约定:方法后缀名应以 Async 结尾。

(6)其它:匿名方法和 Lambda 表达式也可以作为异步对象;async 是一个上下文关键字;关键字 async 必须在返回类型前。

图1 异步方法的简单结构图

  关于 async 关键字:

  ①在返回类型之前包含 async 关键字

  ②它只是标识该方法包含一个或多个 await 表达式,即,它本身不创建异步操作。

  ③它是上下文关键字,即可作为变量名。

  现在先来着重分析一下这三种返回值类型:void、Task 和 Task<T>

  (1)Task<T>:调用方法要从调用中获取一个 T 类型的值,异步方法的返回类型就必须是Task<T>。调用方法从 Task 的 Result 属性获取的就是 T 类型的值。

 1         private static void Main(string[] args)
 2         {
 3             Task<int> t = Calculator.AddAsync(1, 2);
 4
 5             //一直在干活
 6
 7             Console.WriteLine($"result: {t.Result}");
 8
 9             Console.Read();
10         }

Program.cs

 1     internal class Calculator
 2     {
 3         private static int Add(int n, int m)
 4         {
 5             return n + m;
 6         }
 7
 8         public static async Task<int> AddAsync(int n, int m)
 9         {
10             int val = await Task.Run(() => Add(n, m));
11
12             return val;
13         }
14     }

图2

图3

  (2)Task:调用方法不需要从异步方法中取返回值,但是希望检查异步方法的状态,那么可以选择可以返回 Task 类型的对象。不过,就算异步方法中包含 return 语句,也不会返回任何东西。

 1         private static void Main(string[] args)
 2         {
 3             Task t = Calculator.AddAsync(1, 2);
 4
 5             //一直在干活
 6
 7             t.Wait();
 8             Console.WriteLine("AddAsync 方法执行完成");
 9
10             Console.Read();
11         }

Program.cs

 1     internal class Calculator
 2     {
 3         private static int Add(int n, int m)
 4         {
 5             return n + m;
 6         }
 7
 8         public static async Task AddAsync(int n, int m)
 9         {
10             int val = await Task.Run(() => Add(n, m));
11             Console.WriteLine($"Result: {val}");
12         }
13     }

图4

图5

  (3)void:调用方法执行异步方法,但又不需要做进一步的交互。

 1         private static void Main(string[] args)
 2         {
 3             Calculator.AddAsync(1, 2);
 4
 5             //一直在干活
 6
 7             Thread.Sleep(1000); //挂起1秒钟
 8             Console.WriteLine("AddAsync 方法执行完成");
 9
10             Console.Read();
11         }

Program.cs

 1     internal class Calculator
 2     {
 3         private static int Add(int n, int m)
 4         {
 5             return n + m;
 6         }
 7
 8         public static async void AddAsync(int n, int m)
 9         {
10             int val = await Task.Run(() => Add(n, m));
11             Console.WriteLine($"Result: {val}");
12         }
13     }

Calculator.cs

图6

图7

一、控制流

异步方法的结构可拆分成三个不同的区域:

(1)表达式之前的部分:从方法头到第一个 await 表达式之间的所有代码。

(2)await 表达式:将被异步执行的代码。

(3)表达式之后的部分:await 表达式的后续部分。

  图1-1

  该异步方法执行流程:从await表达式之前的地方开始,同步执行到第一个 await,标识着第一部分执行结束,一般来说此时 await 工作还没完成。当await 任务完成后,该方法将继续同步执行后续部分。在执行的后续部分中,如果依然存在 await,就重复上述过程。

  当到达 await 表达式时,线程将从异步方法返回到调用方法。如果异步方法的返回类型为 Task 或 Task<T>,会创建一个 Task 对象,标识需要异步完成的任务,然后将 Task 返回来调用方法。

  图1-2

  异步方法的控制流:

  ①异步执行 await 表达式的空闲任务。

  ②await 表达式执行完成,继续执行后续部分。如再遇到 await 表达式,按相同情况进行处理。

  ③到达末尾或遇到 return 语句时,根据返回类型可以分三种情况:

    a. void:退出控制流。

    b.Task:设置 Task 的属性并退出。

    c.Task<T>:设置 Task 的属性和返回值(Result 属性)并退出。

  ④同时,调用方法将继续执行,从异步方法获取 Task 对象。需要值的时候,会暂停等到 Task 对象的 Result 属性被赋值才会继续执行。

  【难点】

  ①第一次遇到 await 所返回对象的类型。这个返回类型就是同步方法头的返回类型,跟await表达式的返回值没有关系。

  ②到达异步方法的末尾或遇到return 语句,它并没有真正的返回一个值,而是退出了该方法。

二、await 表达式

  await 表达式指定了一个异步执行的任务。默认情况,该任务在当前线程异步执行。

  每一个任务就是一个 awaitable 类的实例。awaitable 类型指包含 GetAwaiter() 方法的类型。

  实际上,你并不需要构建自己的 awaitable,一般只需要使用 Task 类,它就是 awaitable。

  最简单的方式是在方法中使用 Task.Run() 来创建一个 Task。【注意】它是在不同的线程上运行你的方法。

  下面我们来看看示例。

 1     internal class Program
 2     {
 3         private static void Main(string[] args)
 4         {
 5             var t = Do.GetGuidAsync();
 6             t.Wait();
 7
 8             Console.Read();
 9         }
10
11
12         private class Do
13         {
14             /// <summary>
15             /// 获取 Guid
16             /// </summary>
17             /// <returns></returns>
18             private static Guid GetGuid()   //与Func<Guid> 兼容
19             {
20                 return Guid.NewGuid();
21             }
22
23             /// <summary>
24             /// 异步获取 Guid
25             /// </summary>
26             /// <returns></returns>
27             public static async Task GetGuidAsync()
28             {
29                 var myFunc = new Func<Guid>(GetGuid);
30                 var t1 = await Task.Run(myFunc);
31
32                 var t2 = await Task.Run(new Func<Guid>(GetGuid));
33
34                 var t3 = await Task.Run(() => GetGuid());
35
36                 var t4 = await Task.Run(() => Guid.NewGuid());
37
38                 Console.WriteLine($"t1: {t1}");
39                 Console.WriteLine($"t2: {t2}");
40                 Console.WriteLine($"t3: {t3}");
41                 Console.WriteLine($"t4: {t4}");
42             }
43         }
44     }

图2-1

图2-2

  上面 4 个 Task.Run() 都是采用了 Task Run(Func<TReturn> func) 形式。

  Task.Run() 支持 4 中不同的委托类型所表示的方法:Action、Func<TResult>、Func<Task> 和 Func<Task<TResult>>

 1     internal class Program
 2     {
 3         private static void Main(string[] args)
 4         {
 5             var t = Do.GetGuidAsync();
 6             t.Wait();
 7
 8             Console.Read();
 9         }
10
11         private class Do
12         {
13             public static async Task GetGuidAsync()
14             {
15                 await Task.Run(() => { Console.WriteLine(Guid.NewGuid()); });   //Action
16
17                 Console.WriteLine(await Task.Run(() => Guid.NewGuid()));    //Func<TResult>
18
19                 await Task.Run(() => Task.Run(() => { Console.WriteLine(Guid.NewGuid()); }));   //Func<Task>
20
21                 Console.WriteLine(await Task.Run(() => Task.Run(() => Guid.NewGuid())));    //Func<Task<TResult>>
22             }
23         }
24     }

三、取消异步操作

四、异常处理

五、在调用方法中同步地等待任务

六、在异步方法中异步地等待任务

七、Task.Delay 方法

小结

--这是Alpha版本--

时间: 2024-09-28 20:12:00

[C#] 异步编程 - 剖析异步方法的相关文章

异步编程 z

走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $"" 来拼接字符串,相当于string.Format() 方法. 目录 What's 异步? async/await 结构 What’s 异步方法? 一.What's 异步? 启动程序时,系统会在内存中创建一个新的进程.进程是构成运行程序资源的集合. 在进程内部,有称为线程的内核对象,它代表的是

[C#] 走进异步编程的世界 - 开始接触 async/await(转)

原文链接:http://www.cnblogs.com/liqingwen/p/5831951.html 走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $"" 来拼接字符串,相当于string.Format() 方法. 目录 What's 异步? async/await 结构 What’s 异步方法? 一.What's 异步? 启动程序时,系

异步编程(二)基于事件的异步编程模式 (EAP)

一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进度报告的功能,对于有界面的应用程序来说,进度报告和取消操作的支持也是必不可少的,既然存在这样的问题,微软当然也应该提供给我们解决问题的方案了,所以微软在.NET 2.0的时候就为我们提供了一个新的异步编程模型,也就是我这个专题中介绍的基于事件的异步编程模型--EAP. 实现了基于事件的异步模式的类将

[C#] 走进异步编程的世界 - 剖析异步方法(下)

走进异步编程的世界 - 剖析异步方法(下) 序 感谢大家的支持,这是昨天发布<走进异步编程的世界 - 剖析异步方法(上)>的补充篇. 目录 异常处理 在调用方法中同步等待任务 在异步方法中异步等待任务 使用 Task.Delay() 暂停操作 一.异常处理 await 表达式也可以使用 try...catch...finally 结构. 1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 va

[C#]剖析异步编程语法糖: async和await

一.难以被接受的async 自从C#5.0,语法糖大家庭又加入了两位新成员: async和await. 然而从我知道这两个家伙之后的很长一段时间,我甚至都没搞明白应该怎么使用它们,这种全新的异步编程模式对于习惯了传统模式的人来说实在是有些难以接受,不难想象有多少人仍然在使用手工回调委托的方式来进行异步编程.C#中的语法糖非常多,从自动属性到lock.using,感觉都很好理解很容易就接受了,为什么偏偏async和await就这么让人又爱又恨呢? 我想,不是因为它不好用(相反,理解了它们之后是非常

C#~异步编程再续~async异步方法与同步方法的并行

并行编程awit&async相关文章 C#异步编程 今天晚上没事写了个测试的代码,又看了看.net的并行编程,两个方法,一个是异步async修饰的,另一个是普通的方法,在控制台程序的Main方法里去调用这两个方法,会有什么结果呢? 首先我们看一下方法的组成,step1如下 public async void Step1() { try { //await进行等待后,新线程的异常可以被主线程捕捉,这是正常的,下面的代码不会被执行 await Task.Run(() => { Console.W

【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 二.同步代码存在的问题 对于同步的代码,大家肯定都不陌生,因为我们平常写的代码大部分都是同步的,然而同步代码却存在一个很严重的问题,例如我们向一个Web服务器发出一个

.net异步编程の-------异步编程模型(APM)

术语解释: APM               异步编程模型, Asynchronous Programming Model EAP                基于事件的异步编程模式, Event-based Asynchronous Pattern TAP                基于任务的异步编程模式, Task-based Asynchronous Pattern 一.异步编程 APM即异步编程模型的简写(Asynchronous Programming Model),大家在写代

[.NET] 利用 async &amp; await 的异步编程

利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异步提高响应能力 更容易编写的异步方法 异步方法的控制流 线程 async 和 await 返回类型和参数信息 命名的约定 一.异步编程的简介 通过使用异步编程,你可以避免性能瓶颈并增强应用程序的总体响应能力. Visual Studio 2012 引入了一个简化的方法,异步编程,在 .NET Framewo