走进异步编制程序的世界 – 剖析异步方法(上)

走进异步编制程序的社会风气 – 剖析异步方法(上)

一、简介

[C#] 异步编制程序,

  那是上篇《走进异步编制程序的社会风气 – 开始接触 async/await
异步编制程序》(入门)的第二章内容,首借使与我们1块深远商讨下异步方法。

  本文必要理解委托的利用。

 

  那是上篇《走进异步编制程序的世界 – 开始接触 async/await
异步编制程序》(入门)的第一章内容,首倘若与我们一道深刻探究下异步方法。

  本文要求精通委托的运用。

 

  首先来看看.net的向上中的各样阶段的特征:NET 与C#
的每种版本公布都是有二个“核心”。即:C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#肆.0动态语言→C#4.5异步编程

剖析异步方法

目录

  • 介绍异步方法

  • 金沙注册送58,控制流

  • await
    表达式

  • How
    撤销异步操作

 

目录

  • 介绍异步方法

  • 控制流

  • await
    表达式

  • How
    撤除异步操作

 

 

浅析异步方法,异步编制程序。序

  那是上篇《初步接触 async/await
异步编制程序》(入门)的第2章内容,主若是深刻领悟异步方法,提议大家先看入门篇,相当的短。

  本文要求通晓委托的运用。

 

介绍异步方法

     异步方法:在实行到位前及时赶回调用方法,在调用方法继续实施的长河中成就职分。

     语法分析:

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

     (二)供给:包涵N(N>0) 个 await 表达式(不设有 await 表明式的话 IDE
会发出警告),表示必要异步试行的职分。【备注】谢谢 czcz1024 的校正与增补:未有的话,就和日常方法1致进行了。

     (三)重返类型:只可以回去 3种档次(void、Task 和 Task<T>)。Task 和 Task<T>
标志重返的靶子会在以往完毕职业,表示调用方法和异步方法能够继续实行。

     (四)参数:数量不限。但无法选拔 out
和 ref 关键字。

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

     (陆)其余:匿超情势和 Lambda
表达式也得以当做异步对象;async 是三个上下文关键字;关键字 async
必须在回去类型前。

 

金沙注册送58 1图一异步方法的简约结构图

  关于 async 关键字:

  壹在回到类型在此以前包涵 async 关键字

  二它只是标记该措施包括2个或四个 await
表达式,即,它本人不创造异步操作。

  叁它是上下文关键字,就可以作为变量名。

 

  未来先来总结解析一下那三种回到值类型:void、Task 和 Task<T>

  (壹)Task<T>:调用方法要从调用中赢得一个T 类型的值,异步方法的归来类型就非得是Task<T>。调用方法从 Task 的
Result 属性获取的就是 T 类型的值。

金沙注册送58 2金沙注册送58 3

 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

金沙注册送58 4金沙注册送58 5

 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     }

View Code 

金沙注册送58 6

图2

金沙注册送58 7

图3

 

  (贰)Task:调用方法不须要从异步方法中取重返值,但是指望检查异步方法的景色,那么能够挑选能够回到
Task 类型的对象。可是,纵然异步方法中包蕴 return
语句,也不会回来任王大帅西。

金沙注册送58 8金沙注册送58 9

 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

金沙注册送58 10金沙注册送58 11

 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     }

View Code

 

金沙注册送58 12

 

图4

金沙注册送58 13

图5

     

  (3)void:调用方法实施异步方法,但又不必要做越来越的相互。 

金沙注册送58 14金沙注册送58 15

 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

金沙注册送58 16金沙注册送58 17

 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

金沙注册送58 18

图6

金沙注册送58 19

图7

 

介绍异步方法

     异步方法:在实践到位前及时重回调用方法,在调用方法继续实践的进度中做到职责。

     语法分析:

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

     (二)须求:包括N(N>0) 个 await 表明式(不设有 await 表明式的话 IDE
会发出警告),表示须要异步实行的职责。【备注】感激 czcz1024 的匡正与互补:未有的话,就和1般方法1致举办了。

     (3)重返类型:只好回去 3类别型(void、Task 和 Task<T>)。Task 和 Task<T>
标记重返的目的会在后天实现职业,表示调用方法和异步方法能够继续执行。

     (四)参数:数量不限。但无法使用 out
和 ref 关键字。

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

     (陆)别的:匿有名的模特式和 Lambda
表明式也能够用作异步对象;async 是二个上下文关键字;关键字 async
必须在回到类型前。

 

金沙注册送58 20图一 异步方法的总结结构图

  关于 async 关键字:

  壹在回来类型从前包蕴 async 关键字

  二它只是标志该方法包括二个或多个 await
表明式,即,它本人不创立异步操作。

  三它是上下文关键字,就可以作为变量名。

 

  今后先来轻巧分析一下那二种回到值类型:void、Task 和 Task<T>

  (一)Task<T>:调用方法要从调用中收获多个T 类型的值,异步方法的回到类型就不能不是Task<T>。调用方法从 Task 的
Result 属性获取的就是 T 类型的值。

金沙注册送58 21金沙注册送58 22

 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

金沙注册送58 23金沙注册送58 24

 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     }

View Code 

金沙注册送58 25

图2

金沙注册送58 26

图3

 

  (二)Task:调用方法不必要从异步方法中取再次回到值,不过期望检查异步方法的情况,那么能够选用能够回来
Task 类型的靶子。不过,就算异步方法中含有 return
语句,也不会回到任周岚西。

金沙注册送58 27金沙注册送58 28

 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

金沙注册送58 29金沙注册送58 30

 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     }

View Code

 

金沙注册送58 31

 

图4

金沙注册送58 32

图5

     

  (三)void:调用方法实施异步方法,但又不必要做越来越的交互。 

金沙注册送58 33金沙注册送58 34

 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

金沙注册送58 35金沙注册送58 36

 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

金沙注册送58 37

图6

金沙注册送58 38

图7

 

  上面小编来轻便的介绍一下异步编制程序:异步编制程序,在 .NET Framework 四.五 和
Windows 运营时采纳异步协助。
编写翻译器可施行开采职员曾实行的高难度工作,且应用程序保留了1个好像于一块代码的逻辑结构。
因而,你只需做一小部分工作就能够赢得异步编制程序的保有好处。 

目录

 

一、控制流

     异步方法的协会可拆分成多个不一致的区域:

     (一)表明式以前的壹对:从章程头到第多少个await 表达式之间的兼具代码。

     (贰)await
表明式:将被异步实行的代码。

     (3)表明式之后的壹对:await
表明式的继续部分。

 金沙注册送58 39

  图1-1

 

  该异步方法试行流程:从await表明式从前的地点开头,同步实行到第壹个await,标记着第叁局地举行达成,壹般的话此时 await 职业还没做到。当await
职分完毕后,该办法将一连一齐施行后续部分。在试行的持续部分中,假诺依旧留存
await,就再次上述进度。

  当达到 await
表明式时,线程将从异步方法重临到调用方法。假如异步方法的归来类型为 Task
或 Task<T>,会创制五个 Task 对象,标记须求异步完结的义务,然后将
Task 再次回到来调用方法。

 

金沙注册送58 40

  图1-2

  异步方法的调整流:

  一异步试行 await 表达式的悠闲职务。

  贰await 表明式实行到位,继续实施后续部分。如再碰着 await
表明式,按同样景况张开拍卖。

  叁达到末尾或蒙受 return 语句时,根据再次回到类型能够分二种意况:

    a.void:退出调控流。

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

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

  肆同时,调用方法将继续试行,从异步方法赢得 Task
对象。必要值的时候,会暂停等到 Task 对象的 Result
属性被赋值才会继续实践。

 

  【难点】

  1首先次相见 await
所重回对象的品种。这么些再次回到类型正是1块方法头的回到类型,跟 await
表明式的重返值未有涉及。

  2达到异步方法的终极或境遇 return
语句,它并不曾真正的回来叁个值,而是退出了该方法。

 

一、控制流

     异步方法的布局可拆分成八个分歧的区域:

     (一)表明式在此以前的壹些:从点子头到第一个await 表达式之间的享有代码。

     (二)await
表明式:将被异步奉行的代码。

     (三)表达式之后的一些:await
表明式的延续部分。

 金沙注册送58 41

  图1-1

 

  该异步方法实施流程:从await表明式此前的地点伊始,同步实践到第三个await,标记着第1局地进行完成,1般的话此时 await 工作还没完成。当await
任务到位后,该办法将接二连三一齐试行后续部分。在施行的继承部分中,如若仍旧留存
await,就再也上述进程。

  当达到 await
表明式时,线程将从异步方法重回到调用方法。倘使异步方法的回来类型为 Task
或 Task<T>,会创建3个 Task 对象,标志要求异步完毕的职分,然后将
Task 重返来调用方法。

 

金沙注册送58 42

  图1-2

  异步方法的调控流:

  1异步试行 await 表明式的空余职分。

  贰await 表明式施行到位,继续试行后续部分。如再遇上 await
表明式,按同样情形开始展览拍卖。

  叁达到末尾或遇到 return 语句时,依照再次来到类型能够分三种状态:

    a.void:退出调节流。

    b.Task:设置
Task 的个性并脱离。

    c.Task<T>:设置 Task
的习性和重回值(Result 属性)并脱离。

  肆还要,调用方法将继续实行,从异步方法获得 Task
对象。需求值的时候,会停顿等到 Task 对象的 Result
属性被赋值才会继续施行。

 

  【难点】

  壹率先次遇到 await
所重回对象的类型。那些重回类型正是一只方法头的回来类型,跟 await
表明式的重临值未有涉嫌。

  二达到异步方法的最后或境遇 return
语句,它并不曾真正的回来二个值,而是退出了该措施。

 

 

介绍异步方法

   
 异步方法:在推行到位前及时回到调用方法,在调用方法继续试行的历程中形成任务。
     语法分析:      (一)关键字:方法头使用 async 修饰。    
 (二)供给:包涵 N(N>0) 个 await 表明式(不存在 await 表明式的话
IDE
会发出警告),表示供给异步实践的职务。【备注】谢谢 czcz拾贰肆 的校对与补偿:未有的话,就和平常方法一致进行了。
     (三)再次来到类型:只可以回到 三 种档次(void、Task 和
Task<T>)。Task 和 Task<T>
标记再次来到的对象会在未来到位工作,表示调用方法和异步方法能够继续施行。    
 (四)参数:数量不限。但不能够接纳 out 和 ref 关键字。    
 (伍)命名约定:方法后缀名应以 Async 结尾。      (陆)其它:匿有名的模特式和
拉姆da 说明式也能够看做异步对象;async 是1个上下文关键字;关键字 async
必须在回去类型前。     关于 async 关键字:

  1在回来类型从前蕴含 async 关键字

  2它只是标志该办法包涵三个或多个 await
表明式,即,它自己不创设异步操作。

  三它是上下文关键字,就能够作为变量名。

 

  现在先来重点分析一下那二种回到值类型:void、Task 和 Task<T>

  (壹)Task<T>:调用方法要从调用中获得三个 T
类型的值,异步方法的归来类型就非得是Task<T>。调用方法从 Task 的
Result 属性获取的正是 T 类型的值。

金沙注册送58 43 1 private
static void Main(string[] args) 二 { 三 Task<int> t =
Calculator.AddAsync(1, 二); 四 伍 //一贯在工作 6 7Console.WriteLine($”result: {t.Result}”); 8 9 Console.Read(); 10 } Program.cs
金沙注册送58 44 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 } View Code 

 

  (贰)Task:调用方法不要求从异步方法中取再次回到值,不过期望检查异步方法的气象,那么能够选拔能够回来
Task 类型的对象。可是,尽管异步方法中蕴藏 return
语句,也不会回到任何事物。

金沙注册送58 45 1 private
static void Main(string[] args) 贰 { 三 Task t = Calculator.AddAsync(壹,
二); 4 五 //一贯在劳作 六 七 t.Wait(); 八 Console.WriteLine(“AddAsync
方法奉行到位”); 玖 拾 Console.Read(); 1壹 } Program.cs
金沙注册送58 46 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 } View Code

金沙注册送58 47

图4

金沙注册送58 48

图5

     

  (三)void:调用方法实施异步方法,但又不需求做越来越的交互。 

金沙注册送58 49 1 private
static void Main(string[] args) 二 { 三 Calculator.AddAsync(壹, 贰); 四 5
//一贯在办事 陆 7 Thread.Sleep(一千); //挂起1分钟 八Console.WriteLine(“AddAsync 方法实施到位”); 玖 十 Console.Read(); 1一 }
Program.cs
金沙注册送58 50 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

金沙注册送58 51

图6

金沙注册送58 52

图7

 

二、await 表达式

  await 表明式钦赐了四个异步实施的天职。暗中认可景况,该职务在眼下线程异步实行。

  每三个职分就是1个 awaitable 类的实例。awaitable 类型指包罗GetAwaiter() 方法的花色。

  实际上,你并不供给营造筑协会调的 awaitable,1般只必要采纳 Task
类,它正是 awaitable。

  最简便易行的章程是在方式中选择Task.Run() 来创立多少个 Task。【注意】它是在差别的线程上施行办法。

 

  让大家一起来探望示例。

金沙注册送58 53金沙注册送58 54

 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     }

View Code

金沙注册送58 55

图2-1

金沙注册送58 56

图2-2

   下边 4 个 Task.Run() 都以采纳了 Task Run(Func<TReturn> func)
情势来平昔或直接调用 Guid.NewGuid()。

 

  Task.Run() 援助 4 中不相同的信托项目所代表的法子:Action、Func<TResult>、Func<Task>
和 Func<Task<TResult>>

金沙注册送58 57金沙注册送58 58

 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     }

View Code

金沙注册送58 59

图二-3 Task.Run() 方法的重载

 

二、await 表达式

  await 表达式钦定了1个异步施行的职责。暗许意况,该任务在现阶段线程异步实践。

  每2个职分便是三个 awaitable 类的实例。awaitable 类型指包蕴GetAwaiter() 方法的种类。

  实际上,你并不供给营造和谐的 awaitable,壹般只需求动用 Task
类,它就是 awaitable。

  最简易的方法是在章程中央银行使
Task.Run() 来创设二个 Task。【注意】它是在不一样的线程上试行形式。

 

  让咱们一起来看望示例。

金沙注册送58 60金沙注册送58 61

 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     }

View Code

金沙注册送58 62

图2-1

金沙注册送58 63

图2-2

   上面 四 个 Task.Run() 都以行使了 Task Run(Func<TReturn> func)
方式来直接或直接调用 Guid.NewGuid()。

 

  Task.Run() 扶助 四 中差别的寄托项目所代表的主意:Action、Func<TResult>、Func<Task>
和 Func<Task<TResult>>

金沙注册送58 64金沙注册送58 65

 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     }

View Code

金沙注册送58 66

图二-3 Task.Run() 方法的重载

 

  所谓的异步编制程序是采用CPU空闲时间和多核的特征,它所再次来到的Task或Task<TResult>是对await的一个承诺,当任务推行实现后回到3个结果给接收者。那里看到那些大概各位不太掌握,不妨,上边会有教学。

一、控制流

     异步方法的结构可拆分成八个区别的区域:    
 (一)表达式此前的有的:从章程头到第二个 await 表明式之间的兼具代码。  
   (贰)await 表明式:将被异步推行的代码。    
 (三)表达式之后的一对:await 表明式的持续部分。   一 internal class
Program 贰 { 三 private static void Main(string[] args) 四 { 伍 var t =
Do.GetGuidAsync(); 陆 t.Wait(); 7 八 Console.Read(); 玖 } 10 1一 12 private
class Do 一叁 { 1四 /// <summary> 一5 /// 获取 Guid 16 ///
</summary> 一7 /// <returns></returns> 1八 private
static Guid GetGuid() //与Func<Guid> 兼容 1九 { 20 return
Guid.NewGuid(); 二一 } 2二 二3 /// <summary> 贰四 /// 异步获取 Guid 25
/// </summary> 二陆 /// <returns></returns> 二7 public
static async Task GetGuidAsync() 2八 { 2九 var myFunc = new
Func<Guid>(GetGuid); 30 var t1 = await Task.Run(myFunc); 31 3二 var
t二 = await Task.Run(new Func<Guid>(GetGuid)); 3叁 3四 var t3 = await
Task.Run(() => GetGuid()); 3五 3陆 var t四 = await Task.Run(() =>
Guid.NewGuid()); 叁柒 3八 Console.WriteLine($”t一: {t一}”); 3九Console.WriteLine($”t2: {t二}”); 40 Console.WriteLine($”t叁: {t叁}”); 41Console.WriteLine($”t四: {t4}”); 4二 } 四三 } 4四 } View Code

图2-1

图2-2

   上边 四 个 Task.Run() 都是应用了 Task Run(Func<TReturn> func)
情势。

 

  Task.Run() 支持 肆中差别的寄托项目所代表的格局:Action、Func<TResult>、Func<Task>
和 Func<Task<TResult>>

金沙注册送58 67 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 }
View Code

 

三、How 撤废异步操作

   CancellationToken 和 CancellationTokenSource
那三个类允许你打住实施异步方法。

  (1)CancellationToken
对象涵盖任务是还是不是被撤销的音信;若是该指标的性情 IsCancellationRequested

true,义务需终止操作并回到;该对象操作是不可逆的,且不得不采用(修改)二次,即该对象内的 IsCancellationRequested
属性棉被服装置后,就不可能更换。

  (二)CancellationTokenSource 可创立 CancellationToken
对象,调用 CancellationTokenSource 对象的 Cancel
方法,会使该目的的 CancellationToken 属性 IsCancellationRequested 设置为
true。

  【注意】调用 CancellationTokenSource
对象的 Cancel
方法,并不会举办打消操作,而是会将该对象的 CancellationToken
属性 IsCancellationRequested 设置为 true。

 

  示例

金沙注册送58 68金沙注册送58 69

 1     internal class Program
 2     {
 3         private static void Main(string[] args)
 4         {
 5             CancellationTokenSource source = new CancellationTokenSource();
 6             CancellationToken token = source.Token;
 7 
 8             var t = Do.ExecuteAsync(token);
 9 
10             //Thread.Sleep(3000);   //挂起 3 秒
11             //source.Cancel();    //传达取消请求
12 
13             t.Wait(token);  //等待任务执行完成
14             Console.WriteLine($"{nameof(token.IsCancellationRequested)}: {token.IsCancellationRequested}");
15 
16             Console.Read();
17         }
18 
19 
20     }
21 
22     internal class Do
23     {
24         /// <summary>
25         /// 异步执行
26         /// </summary>
27         /// <param name="token"></param>
28         /// <returns></returns>
29         public static async Task ExecuteAsync(CancellationToken token)
30         {
31             if (token.IsCancellationRequested)
32             {
33                 return;
34             }
35 
36             await Task.Run(() => CircleOutput(token), token);
37         }
38 
39         /// <summary>
40         /// 循环输出
41         /// </summary>
42         /// <param name="token"></param>
43         private static void CircleOutput(CancellationToken token)
44         {
45             Console.WriteLine($"{nameof(CircleOutput)} 方法开始调用:");
46 
47             const int num = 5;
48             for (var i = 0; i < num; i++)
49             {
50                 if (token.IsCancellationRequested)  //监控 CancellationToken
51                 {
52                     return;
53                 }
54 
55                 Console.WriteLine($"{i + 1}/{num} 完成");
56                 Thread.Sleep(1000);
57             }
58         }
59     }

View Code

金沙注册送58 70

图3-1

金沙注册送58 71

图3-2 注释两行代码

金沙注册送58 72

图三-3:图三-一和图叁-二的奉行结果(注释两行代码)

  上航海用图书馆是不调用 Cancel() 方法的结果图,不会收回任务的实践。

 

  下图在 三 秒后调用 Cancel() 方法打消任务的实践:

金沙注册送58 73

图3-四:去掉注释

金沙注册送58 74

图3-五:图3-1和图3-四的施行结果(去掉注释)

 

三、How 撤消异步操作

   CancellationToken 和 CancellationTokenSource
那八个类允许你打住施行异步方法。

  (壹)CancellationToken
对象涵盖职责是或不是被撤消的新闻;借使该对象的品质 IsCancellationRequested

true,职务需终止操作并赶回;该目的操作是不可逆的,且只好利用(修改)壹回,即该目的内的 IsCancellationRequested
属性被安装后,就不可能改换。

  (二)CancellationTokenSource 可创设 CancellationToken
对象,调用 CancellationTokenSource 对象的 Cancel
方法,会使该目的的 CancellationToken 属性 IsCancellationRequested 设置为
true。

  【注意】调用 CancellationTokenSource
对象的 Cancel
方法,并不会施行裁撤操作,而是会将该对象的 CancellationToken
属性 IsCancellationRequested 设置为 true。

 

  示例

金沙注册送58 75金沙注册送58 76

 1     internal class Program
 2     {
 3         private static void Main(string[] args)
 4         {
 5             CancellationTokenSource source = new CancellationTokenSource();
 6             CancellationToken token = source.Token;
 7 
 8             var t = Do.ExecuteAsync(token);
 9 
10             //Thread.Sleep(3000);   //挂起 3 秒
11             //source.Cancel();    //传达取消请求
12 
13             t.Wait(token);  //等待任务执行完成
14             Console.WriteLine($"{nameof(token.IsCancellationRequested)}: {token.IsCancellationRequested}");
15 
16             Console.Read();
17         }
18 
19 
20     }
21 
22     internal class Do
23     {
24         /// <summary>
25         /// 异步执行
26         /// </summary>
27         /// <param name="token"></param>
28         /// <returns></returns>
29         public static async Task ExecuteAsync(CancellationToken token)
30         {
31             if (token.IsCancellationRequested)
32             {
33                 return;
34             }
35 
36             await Task.Run(() => CircleOutput(token), token);
37         }
38 
39         /// <summary>
40         /// 循环输出
41         /// </summary>
42         /// <param name="token"></param>
43         private static void CircleOutput(CancellationToken token)
44         {
45             Console.WriteLine($"{nameof(CircleOutput)} 方法开始调用:");
46 
47             const int num = 5;
48             for (var i = 0; i < num; i++)
49             {
50                 if (token.IsCancellationRequested)  //监控 CancellationToken
51                 {
52                     return;
53                 }
54 
55                 Console.WriteLine($"{i + 1}/{num} 完成");
56                 Thread.Sleep(1000);
57             }
58         }
59     }

View Code

金沙注册送58 77

图3-1

金沙注册送58 78

图三-二 注释两行代码

金沙注册送58 79

图三-三:图三-一和图叁-二的实践结果(注释两行代码)

  上海教室是不调用 Cancel() 方法的结果图,不会收回职务的实行。

 

  下图在 三 秒后调用 Cancel() 方法撤销职务的实践:

金沙注册送58 80

图三-四:去掉注释

金沙注册送58 81

图三-伍:图三-壹和图三-4的推行结果(去掉注释)

 

 

3、裁撤异步操作

 

小结

  • 介绍异步方法的语法、二种分化的回来值类型(void、Task 和
    Task<T>)和垄断流程等。
  • 轻巧常用的异步实施措施:Task.Run()。【注意】它是在不一样的线程上试行格局。
  • 何以撤消异步操作。

 

小结

  • 介绍异步方法的语法、二种分裂的归来值类型(void、Task 和
    Task<T>)和调节流程等。
  • 总结常用的异步实行办法:Task.Run()。【注意】它是在分裂的线程上实施方式。
  • 怎么着打消异步操作。

 

贰、使用验证

4、万分处理

 

传送门

  入门:《初叶接触 async/await
异步编程》

  补充篇:《走进异步编制程序的社会风气 –
剖析异步方法(下)》

  GUI 篇:《走进异步编制程序的社会风气 – 在 GUI
中实施异步操作》

 


初稿链接:

 【参考】《Illustrated C# 2012》

传送门

  入门:《开班接触 async/await
异步编制程序》

  补充篇:《走进异步编制程序的世界 –
剖析异步方法(下)》

  GUI 篇:《走进异步编制程序的社会风气 – 在 GUI
中实践异步操作》

 


初稿链接:

 【参考】《Illustrated C# 2012》

情势具名包含一个 Async 或 async 修饰符。

5、在调用方法中协同地守候任务

 

 

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

 

依照约定,异步方法的称号以“Async”后缀结尾。

七、Task.Delay 方法

 

 

小结

 

重回类型为下列项目之壹:

传送门

  入门:《伊始接触 async/await 异步编制程序》

 


 

–那是预览版本,最后整理完核查后将放上首页,不便之处请见谅–

] 异步编制程序, 剖析异步方法 序
那是上篇《开首接触 async/await
异步编制程序》(入门)的第3章内容,重若是深远理解异步方法,提出我们先…

 

1旦您的措施有操作数为 TResult 类型的归来语句,则为
Task<TResult>。

 

假如你的法子未有回去语句或有所未有操作数的回到语句,则为 Task。

 

借使你编写的是异步事件处理程序,则为 Void(Visual Basic 中为 Sub)。

 

关于详细新闻,请参见本主旨后边的“重回类型和参数”。

 

方法一般包涵至少三个 await
表明式,该表达式标识二个点,在该点上,直到等待的异步操作实现措施本领持续。
同时,将艺术挂起,并且控件再次回到到点子的调用方。(那里所谓的挂起便是上文所波及的承诺,异步方法承诺会给调用方多个结实)

 

三、示例

  实行才是检查真理的特等路线。

 

 

using System;

using System.Diagnostics;

using System.Net.Http;

using System.Threading.Tasks;

 

namespace 异步递归

{

    class Program

    {

        static void Main(string[] args)

        {

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            ConsoleAsync1();

            stopwatch.Stop();

            Console.WriteLine(“同步方法用时:” +
stopwatch.ElapsedMilliseconds);

            stopwatch.Reset();

            stopwatch.Start();

            ConsoleAsync();

            stopwatch.Stop();

            Console.WriteLine(“异步方法用时:”+
stopwatch.Elapsed米尔iseconds);

 

            Console.Read();

        }

 

        private static async void ConsoleAsync()

        {

            Console.WriteLine(“异步方法初步”);

            Console.WriteLine(“Result:” + await SumAsync(10));

            Console.WriteLine(“异步方法截至”);

        }

        private static async Task<int> SumAsync(int part)

        {

            if ((part += 10) >= 100)

            {

                return 100;

            }

            HttpClient client = new HttpClient();

            Task<string> getStringTask =
client.GetStringAsync(“”);

            Console.WriteLine(DateTime.Now.Millisecond + ” 异步 ” +
(await getStringTask).Length);

            return await SumAsync(part);

        }

 

        private static void ConsoleAsync1()

        {

            Console.WriteLine(“同步方法伊始”);

            Console.WriteLine(“Result:” + SumAsync1(10));

            Console.WriteLine(“同步方法甘休”);

        }

 

        private static int SumAsync1(int part)

        {

            if ((part += 10) >= 100)

            {

                return 100;

            }

            HttpClient client = new HttpClient();

            Task<string> getStringTask =
client.GetStringAsync(“”);

            Console.WriteLine(DateTime.Now.Millisecond + ” 同步 ” +
getStringTask.Result.Length);

            return SumAsync1(part);

        }

    }

}

 

  示例介绍:

 

    ①、那个事例中有两种达成方式:(一)利用异步编制程序的秘技达成(②)利用平时同步方式贯彻

 

    贰、同时这些事例中贯彻了递归,这几个能够不要思念,博主只是想说Bellamy下在异步的事态下,递归是不是可行而已,实验结果为有效。

 

    三、这段代码中的GetStringAsync()方法是获得远程分界面内容用的,首要目标是延长响应时间。

 

  在应用102线程编写端口扫描程序时,小编本身以为同步和规定全部线程都推行完的光阴是3个相比麻烦的标题。有园友评论说今后曾经不手动创设thread对象了,而是径直行使Task异步格局,作者的网络编制程序老师也讲到了异步编制程序的优越性。在念书了课本上的学识后,举办了二个总结分享给大家。从.NET四.五开首,用async和await关键字再加上Task.Run是1个11分正确的异步编程模型。

1.await和async

  异步方式从本事上看正是接纳委托来促成的,它的重中之重利润是在异步实行的进程中,用户如故能够操控UI分界面。使用Task类和选用Thread类有这么些相似的地点,Task类也是经过调用方法去落实二个义务的产生,方法但是是命名方式或匿名情势,在实施进度中可使用async和await来兑现异步实施。async是1个修饰符,它不得不用在艺术照轶事件处理程序的签订契约中。对于艺术可分为有再次回到值和无再次回到值三种情状,事件则只有1种,如上边3条语句所示:

    private async Task<int> MethodAsync();//有再次来到值的异步方法

    private async Task MethodAsync();//无重临值的异步方法

    private async void btnOk_Click();//异步事件处理程序

  await是3个运算符,它象征等待异步推行的结果。也得以清楚为await运算符实际上是对艺术的重返值举办操作,也便是对Task<Result>进行操作,而不是对艺术本身举行操作。还有某个要小心,await是迟早要放在异步方法的内部,假如未有放在中间的话,VS会活动报错。以下是async和await使用的例证: 

    private async void button5_Click(object sender, EventArgs e)
    {
      Task a = Method1Async();
      //此处可继续试行别的代码
      await a;//等待职务a实现
      Task<int> b = Method2Async();
      //此处可继续实施别的代码
      int c = await b;//等待职务b落成,且能够得到职责b的重回值
    }

    Task Method1Async();
    async Task<int> Method2Async()
    {
      await Task.Delay(100);
      return 1;
    }

  await和一齐编程最大的不一致之处是:异步等待义务完毕的时候,在不会继续试行后边的代码时,也不会潜移默化分界面包车型大巴操作。在.NET提供的类中,异步方法都以预订用Async作为后缀,这样可以很清楚的通晓那几个方法是异步方法依然一块方法。

二. 创造职责

  创造职责也正是将职责与要实践的方法联系起来,编写任务施行的法午时,那些艺术既能够是联合方法也得以是异步方法,还足以是匿超方式。试行异步方法时,必须用async和Task共同表示不曾重返值的任务,用async和Task<TResult>共同表示重临值为TResult的任务。以下是概念试行任务的艺术。

    private async void button5_Click(object sender, EventArgs e)
    {

      //Task.Run方法表示使用暗许的职责调度程序在线程池中通过后台实行钦命的职分

      //就算不需求协调去调度措施,使用那几个艺术最有益
      await Task.Run(()=>Method1Async());//推行同步方法
      int c = await
Task.Run(()=>Method②Async());//实行异步方法
      await Task.Run(async () => { c = 2;
});//推行异步匿名方式
    }
    void Method1Async();
    async Task<int> Method2Async(){…}

  Task.Run方法常用的重载情势有以下4种,其余它也是足以用new关键字显得创立职责,不过那种艺术用的不多。

    Task Run(Func<Task> function);//实践不带重临值的职务

    Task<TResult>
Run<TResult>(Func<Task<TResult>>
function);//试行带重回值的任务

    Task<TResult> Run<TResult>(Func<Task<TResult>>
function, CancellationToken
cancellationToken);//施行进度中得以监听撤除公告

    Task Run(Func<Task> function, CancellationToken
cancellationToken);//执行进度中能够监听撤消布告

3. 悬停职责

  在奉行职分时必然会并发要求结束职务的气象,那里的停下告诉任务你要趁早停下来不再试行了,而不是直接销毁职分实例。那里能够打个比方,学生一同出去吃饭了,学生与导师都在班群里面,突然班群里老师说要让同学们汇聚,假如具有同学都见到那些新闻,然后学生们早先起身,这样就能够正确的集纳。上边的例子有二个很重点的前提,那正是有着同学都要察看那几个音信,也正是学员是随时监听音讯的。CancellationTokenSource类和CancellationToken结构用于落到实处10贰线程、线程池和Task职责的吊销操作,处理形式与地点的事例相似。创制的班群正是CancellationTokenSource对象,收到的通报正是CancellationToken对象。CancellationTokenSource用于制造撤除布告,CancellationToken则用来传播应撤废操作的关照,当调用职分前,能够先创立撤消源对象CancellationTokenSource
cts=new CancellationTokenSource();,借使期待在30秒后自动发出裁撤公告,可以流传参数CancellationTokenSource(TimeSpan.FromSeconds(30));CancellationToken
ct=cts.Token;,后一句代码是获得撤除的关照。CancellationTokenSource还有二个Cancel方法,将这性子子设为true时,该方法会将富有增多了打消标志的CancellationToken对象的IsCancellationRequested属性都安装为true,那样撤除布告就传递到了正在推行的职责。

  职分收到撤销文告后,能够接纳三种方法来终止操作。第1种办法是归纳的从委托返回。那种完结情势接近于在调用职分的代码中三个bool值来表示撤除通告,职务收到后就径直回到了。当使用那种办法时职务意况的再次来到值为TaskStatus.RanToCompletion枚举值,它代表正常落成,而不是TaskStatus.Canceled枚举值。第二种方法是在代码里抓住OperationCanceledException万分,并将其传递到在其上呼吁了裁撤的符号,选取这种办法收回的天职会转移为用Canceled枚举值表示的情况。达成引发那多少个的首要推荐办法是调用ct.ThrowIfCancellationRequestes();。以下是代码示例,写了两个winform程序,利用进程条来打消任务。第一个图是绝非吸引那么些时,程序退出for循环,推行后边的代码后归来了,第三张图是第三种艺术,引发了特别后直接跳转到catch语句块了。

金沙注册送58 82

金沙注册送58 83

金沙注册送58 84

     CancellationTokenSource cts;
        private async void button3_Click(object sender, EventArgs e)
        {
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;
            cts = new CancellationTokenSource();
            var aa = MYThreadAsync("a", cts.Token);
            try
            {
                await aa;
                listBox1.Items.Add("await后面");
            }
            catch
            {
                if (aa.IsCanceled)
                    listBox1.Items.Add("a取消");
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            cts.Cancel();
        }

        public async Task MYThreadAsync(string s, CancellationToken ct)
        {
            for (int i = 0; i < 50; i++)
            {
                if (ct.IsCancellationRequested)
                    break;          //点击关闭按钮,IsCancellationRequested就为true,就会退出for循环,这是第一种方式
                progressBar1.Value += 2;
                await Task.Delay(100);
                ct.ThrowIfCancellationRequested();//这是第二种方式,它会终止任务并且返回catch语句块里面
            }
            listBox1.Items.Add("任务" + s + "完成了");
        }

金沙注册送58 85

4. 得到职分试行的情形

  在异步编制程序中,很鲜明职务实行的图景是2个不行重要的参数。在义务的生命周期里,可以经过Status属性来获得职务实践的意况,当职务达成后还足以由此职责属性知道任务成功的事态。可利用任务实例的Status属性获取职责奉行的情状,职务推行的状态用TaskStatus枚举表示,以下是TaskStatus的枚举值:

Created:职务现已起始化,但从不进入调度安插

WaitingForActivation:该任务已进入调度安插,正在等候被调度程序激活

WaitingToRun:该职分已被调度程序激活,但未有开端举办

Running:该职责正在周转,但从未达成

RanToCompletion:该义务已经打响做到

Canceled:该职责由于被打消而产生,引发那二个或调用方已向该任务的CancellationToken发出时域信号

Faulted:该职务因为出现未经处理的丰盛而到位

WaitingForChildrenToComplete:该职务自笔者已形成,正等待附加的子职分成功

  任务完结景况有关的性格有IsCompleted、IsCanceled和IsFaulted等属性,从单词意思上看简单精通它们的乐趣,个中要专注IsCompleted属性表示任务是或不是落成,无论是不荒谬甘休照旧因为撤销或尤其而形安特卫普为成功。

伍. 职务实践的速度

  有时候我们盼望让有个别异步操作提供进程通报,以便在分界面中显示异步操作实行的速度,能够用Progress<T>类来获得任务施行的进程。以下是行使方式里的Report方法将艺术内变量的值传回创制职分的风波代码里,从而立异进度条的值。

金沙注册送58 86

     CancellationTokenSource cts;
        private async void button3_Click(object sender, EventArgs e)
        {
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;
            cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;
            var pp = new Progress<int>();
            pp.ProgressChanged += (s, n) => {
                progressBar1.Value = n;
            };
            var tt = Task.Run(()=>MYThreadAsync(pp,cts.Token,500),cts.Token);
            try
            {
                await tt;
                if (tt.Exception == null)
                    listBox1.Items.Add("任务完成");
            }
            catch (Exception ex)
            {
                listBox1.Items.Add("异常" + ex.Message);
            }
        }         
        private void button4_Click(object sender, EventArgs e)
        {
            cts.Cancel();
        }
        public  void MYThreadAsync(IProgress<int> progress, CancellationToken ct, int delay)
        {
            int p = 0;//进度
            while (p < 100 && ct.IsCancellationRequested == false)
            {
                p += 1;
                Thread.Sleep(delay);
                progress.Report(p);//这个方法将会触发ProgressChanged事件更新进度条
            }
        }

金沙注册送58 87

六. 按期完结义务

  无论是服务器照旧客户端,都是有定期完结有些职务的内需的。System.Timers.Timer类是三个没有错的定期设置类,这些类能够吸引事件,但它私下认可是在线程池中掀起风浪,而不是在现阶段线程中引发轩然大波。Timer类的常用属性有AutoReset和Interval属性,AutoReset是获得或设置1个bool值,该值为true表示每一次间隔截止时都掀起一次Elapsed风云,false表示仅在第2回间隔截至时引发三次该事件。Interval属性是获取或安装两回Elapsed事件的间隔时间,该值必须超越零并小于Int.马克斯Value,暗许值为十0皮秒。Timer类还有八个常用艺术这正是Start和Stop方法。

  还有3个System.Threading.Timer类,它也是在线程池中定期进行职责,它与前三个Timer类的区别是此类不接纳事件模型,而是径直通过TimerCallback类型的信托来兑现的。该类的构造函数为:Timer(提姆erCallback
callback,Object state,TimeSpan douTime,提姆eSpan
period)。callback表示要进行的措施,state代表多少个暗含回调方法要利用的新闻的靶子,dueTime是第壹遍调用回调方法在此以前延迟的时辰,period表示每一趟调用回调方法的时间距离,-一表示终止。那样创制对象后,首次到达dueTime延时时间会自动调用一遍callback委托,以往每隔period时间间隔调用1遍。以下是那二种方法的运营效果和源代码。金沙注册送58 88

 

金沙注册送58 89

     System.Timers.Timer timer;
        System.Threading.Timer threadtimer;
        private void button2_Click(object sender, EventArgs e)//Timers.Timer
        {
            progressBar1.Maximum = 100;
            progressBar1.Value = 0;
            int pro=0;
            timer = new System.Timers.Timer(500);
            timer.AutoReset = true;
            timer.Elapsed+= (obj, args) =>
            { 
                pro+=5;
                progressBar1.Value = pro;
            };
            timer.Start();
        }

       private void button5_Click(object sender, EventArgs e)
       {
           timer.Stop();
           listBox1.Items.Add("第一个已经停止");
       }
    //Threading.Timer类
       private void button1_Click(object sender, EventArgs e)
       {
           progressBar2.Maximum = 100;
           progressBar2.Value = 0;
           TimeSpan dueTime = new TimeSpan(0, 0, 0, 1);
           TimeSpan period = new TimeSpan(0, 0, 0, 0, 200);
           System.Threading.TimerCallback timecall = new TimerCallback((obj) => progressBar2.Value += 5);
           threadtimer = new System.Threading.Timer(timecall, null, dueTime, period);
       }

       private void button6_Click(object sender, EventArgs e)
       {
           threadtimer.Dispose();
           listBox1.Items.Add("第二个已经停止");
       }

金沙注册送58 90

  那篇小说只总计了单个职务的异步推行的根基,还得继续深造多职务并行施行。要是有更加好的才干恐怕与信用合作社应用有关的异步工夫,希望园友能够提出自身继续上学。

相关文章

网站地图xml地图