在此以前商量过c#的async和await关键字,幕后干了怎么样,可是不亮堂为什么找不到相关资料了。今后再也斟酌叁回,顺便记录下来,方便以后翻看。

初稿地址: 

[.NET] 利用 async & await 的异步编制程序,.net利用async

利用 async & await 的异步编制程序

【博主】反骨仔    【出处】   

基础知识

async
关键字标注一个艺术,该方法再次回到值是3个Task、只怕Task<TResult>、void、包罗GetAwaiter方法的体系。该措施一般蕴涵2个await表明式。该表明式标注1个点,将被某些异步方法回跳到该点。并且,当前函数执行到该点,将立刻回到控制权给调用方。

如上描述了async方法想干的工作,至于怎么实现,那里就不阅读了。

 

运用 async & await 的异步编制程序

【博主】反骨仔    【出处】   

目录

  • 异步编制程序的简介
  • 异步升高响应能力
  • 更便于编写的异步方法
  • 异步方法的控制流(核心)
  • 异步中的线程
  • async 和 await
    修饰符
  • 回来类型和参数音讯
  • 命名的预订

 

个人见解

通过能够精通,async
和await关键字首要目标是为了操纵异步线程的协同,让一个异步进度,表现得就像是一块进程一样。

比如说async
方法分n个职务去下载网页并开始展览处理:先await下载,然后即刻回去调用方,之后的拍卖就由异步线程实现下载后调用。那时候调用方能够继续执行它的天职,可是,假设调用方立即就需求async的结果,那么相应就只可以等待,然而超越4/八情景:他近期不必要这些结果,那么就足以并行处理那个代码。

足见,并行性浮未来await 上,就算await
点和终极的多寡结果偏离越远,那么并行度就越高。如若await的点越多,相信也会创新并行性。

材质展现,async 和await
关键字并不会成立线程,那是很首要的有些。
她们只是创制了一个回来点,提供给需求她的线程使用。那么线程毕竟是何人制造?注意await
表明式的重组,他索要一个Task,三个Task并不意味一定要创立线程,也能够是另三个async方法,可是层层包裹最中间的主意,很恐怕正是2个原生的Task,比如await
Task.Run(()=>Thread.Sleep(0));
,这几个实在发生线程的话语,就会根据前面那些await点,每个回调。

从这一点来看,async
方法,未必正是七个异步方法,他在语义上特别接近“非阻塞”,
当际遇阻塞操作,即刻用await定点重临,至于此外更深1层的缓解手段,它就不爱抚了。那是程序员供给关心的,程序员要求用真的的始建线程代码,来成功异步操作(当然这一步可由库程序员完结)。

注意async的多少个重回值类型,这意味着了分化的应用情况。倘使是void,表明客户端不关怀数据同步难点,它只须求线程的控制权马上回去。能够用在ui
等场地,如若是Task,客户端也不关心数据,不过它仰望能够控制异步线程,那只怕是对任务执行种种有必然的渴求。当然,最广泛的是Task<TResult>。

综上,async和await并不是为着多职责而规划的,假若追求高产出,应该在async函数内部用Task好好设计一番。在使用async
和await的时候,只须求依据非阻塞的思绪去编写代码就足以了,至于幕后怎么处理就交给真正的四线程代码创立者吧。

协助实行编制程序与异步编制程序

日常景况下,我们写的C#代码就是1起的,运营在同1个线程中,从程序的率先行代码到最终一句代码顺序执行。而异步编程的为主是行使十二线程,通过让分裂的线程执行不一的天职,完结差异代码的并行运营。

目录

  • 普遍的异步形式async,关于异步执行。异步编制程序的简介
  • 异步提升响应能力
  • 更易于编写的异步方法
  • 异步方法的控制流(宗旨)
  • 线程
  • async 和 await
  • 重返类型和参数音信
  • 命名的预订

 

一、异步编制程序的简介

  通过使用异步编制程序,你可以制止质量瓶颈并增强你的应用程序的全部响应能力。

  从
VS 2011 初步,新引入了3个简化的秘诀,称为异步编制程序。我们在 >= .NET
4.五 如月 Windows 运营时中利用异步,编写翻译器它会支持了我们下降了1度实行的高难度异步代码编写的行事,但逻辑结构却接近于联合代码。由此,大家仅要求举行一小部分编制程序的劳作就能够收获异步编制程序的兼具优点。

 

演示代码

        static async Task RunTaskAsync(int step)
        {
            for(int i=0; i < step; i++)
            {
                await Task.Run(()=>Thread.Sleep(tmloop));//点是静态的,依次执行
                Thread.Sleep(tm2);
            }
            Thread.Sleep(tm3);
        }

//客户端
            Task tk= RunTaskAsync(step);
            Thread.Sleep(tm1);//这一段是并行的,取max(函数,代码段)最大时间
            tk.Wait( );//这里代表最终数据

为了达到惊人并行,应该用真的的二十四线程代码:

        static async Task RunTaskByParallelAsync(int step)
        {
            await Task.Run(()=>Parallel.For(0,step,
                s=>{loop(tmloop);
                    loop(tm2);
                    }
            ));
            loop(tm3);
        }

前台线程与后台线程

有关10二线程,早在.NET二.0时代,基础类库中就提供了Thread达成。暗许景况下,实例化3个Thread创制的是前台线程,只要有前台线程在运营,应用程序的历程就径直处于运行状态,以控制台应用程序为例,在Main方法中实例化一个Thread,那个Main方法就会等待Thread线程执行完结才脱离。而对此后台线程,应用程序将不思虑其是或不是履行达成,只要应用程序的主线程和前台线程执行完成就足以脱离,退出后有所的后台线程将被机关终止。来看代码应该更明了1些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("主线程开始");
 
            //实例化Thread,默认创建前台线程
            Thread t1 = new Thread(DoRun1);
            t1.Start();
 
            //可以通过修改Thread的IsBackground,将其变为后台线程
            Thread t2 = new Thread(DoRun2) { IsBackground = true };
            t2.Start();
 
            Console.WriteLine("主线程结束");
        }
 
        static void DoRun1()
        {
            Thread.Sleep(500);
            Console.WriteLine("这是前台线程调用");
        }
 
        static void DoRun2()
        {
            Thread.Sleep(1500);
            Console.WriteLine("这是后台线程调用");
        }
    }
}

运维方面包车型客车代码,能够见见DoRun二方法的打字与印刷消息“那是后台线程调用”将不会被出示出来,因为应用程序执行完主线程和前台线程后,就活动退出了,全部的后台线程将被活动终止。那里后台线程设置了等待1.伍s,假设那一个后台线程比前台线程或主线程提前实施完结,对应的新闻“那是后台线程调用”将能够被成功打字与印刷出来。

一、异步编制程序的简介

  通过行使异步编制程序,你能够免止质量瓶颈并抓实应用程序的完整响应能力。

  Visual
Studio 二〇一三 引入了二个简化的不二法门,异步编制程序,在 .NET Framework 四.五 和
Windows 运转时使用异步援助。编写翻译器可实施开发职员曾展开的高难度工作,且应用程序保留了三个近乎于联合代码的逻辑结构。因而,您仅要求展开一小部分办事就能够取得异步编制程序的有着优点。

 

二、异步进步响应能力

  异步对恐怕滋生短路的位移(如访问
Web 时),对
Web 财富的走访有时过慢或延迟过高。若那种职分在共同进程中受阻,则全部应用程序必须等待响应达成。 在行使异步的进程中,大家的应用程序可继续执行不依赖Web
财富的其它工作,并会一向等待绿灯的任务顺遂达成。

  那是局地博学多闻的施用异步的施用场景,以及一些在
.NET >= 四.5 后新增的类库。

金沙注册送58 1

  全部与用户界面相关的操作平日共享贰个线程,所以采取异步对于使用 UI
线程的 App 来说是充裕关键的。

  尽管说你的 App
全体操作都以壹起的,也正是说,当二个线程出现堵塞,别的线程都会出现堵塞,更要紧的是,
App 会甘休响应。

金沙注册送58 2

 

  使用异步方法时,App
将继续响应
UI。如:最大和最小化,不过效果照旧在后台执行(如:下载)。

 

并行编码方法

并行执行有多少个方法,第多少个是创制n个Task,壹起运转。难题是怎么处理await点。每种task写二个await点是老大的,因为蒙受第3个await就及时回到,而不会张开全部职务并行执行。由此await无法随便放。那么什么样为一组Task设定await点呢?能够因此Task.WhenAll
那么些主意,他会等待一组Task执行完成重临。

特定情景下,能够用Parallel.For
来拉开壹组任务,但是那么些类并从未落到实处async形式,也正是它会卡住当前线程,所以必要用2个Task来包裹它。

足见,非阻塞和交互不完全是3遍事。

Task

.NET
4.0推出了新一代的三十二线程模型Task。async/await性格是与Task紧凑相关的,所以在理解async/await前务必足够了然Task的使用。那里将以八个简单的德姆o来看一下Task的应用,同时与Thread的创建方式做一下比照。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
 
namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("主线程启动");
 
            //.NET 4.5引入了Task.Run静态方法来启动一个线程
            Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("Task1启动"); });
 
            //Task启动的是后台线程,假如要在主线程中等待后台线程执行完毕,可以调用Wait方法
            Task task = Task.Run(() => { Thread.Sleep(500); Console.WriteLine("Task2启动"); });
            task.Wait();
 
            Console.WriteLine("主线程结束");
        }
    }
}
 
Task的使用

先是,必须明显一点是Task运营的线程是后台线程,可是可以经过在Main方法中调用task.Wait()方法,使应用程序等待task执行达成。Task与Thread的三个首要区分点是:Task底层是使用线程池的,而Thread每一回实例化都会创立一个新的线程。那里能够经过那段代码做三回注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
 
namespace TestApp
{
    class Program
    {
        static void DoRun1()
        {
            Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);
        }
 
        static void DoRun2()
        {
            Thread.Sleep(50);
            Console.WriteLine("Task调用Thread Id =" + Thread.CurrentThread.ManagedThreadId);
        }
 
        static void Main(string[] args)
        {
            for (int i = 0; i < 50; i++)
            {
                new Thread(DoRun1).Start();
            }
 
            for (int i = 0; i < 50; i++)
            {
                Task.Run(() => { DoRun2(); });
            }
 
            //让应用程序不立即退出
            Console.Read();
        }
    }
}
 
Task底层使用线程池

运作代码,能够看看DoRun一()方法每趟的Thread
Id都是区别的,而DoRun二()方法的Thread
Id是重复出现的。大家驾驭线程的成立和销毁是三个付出相比较大的操作,Task.Run()每趟执行将不会立刻创设3个新线程,而是到CL科雷傲线程池查看是还是不是有空余的线程,有的话就取叁个线程处理这一个请求,处理完请求后再把线程放回线程池,那些线程也不会及时撤除,而是设置为空闲状态,可供线程池再度调度,从而减少支出。

二、异步升高响应能力

  异步对只怕引起短路的移动(例如应用程序访问
Web 时)至关心注重要。对
Web 能源的拜访有时非常的慢或会推迟。纵然此类活动在壹起进度中受阻,则整个应用程序必须等待。 在异步进程中,应用程序可继续执行不信赖Web 财富的任何干活,直至潜在阻塞的职务达成。

  下图彰显了异步编制程序提升响应能力的独立应用场景。蕴含从
.NET Framework 四.伍 和 Windows
运维时中列出的壹部分暗含援救异步编制程序的措施的类。

  由于拥有与用户界面相关的移位1般共享2个线程,由此,异步对走访
UI 线程的应用程序来说越发重要。 固然在二个体协会同应用程序中有其余的线程被封堵了,那么具有线程都将被封堵,再严重一点,你的应用程序将会告一段落响应。

  使用异步方法时,应用程序将继承响应
UI。例如,你能够调整窗口的轻重缓急或最小化窗口;如若你不希望等待应用程序结束,则足以将其关闭。

 

3、更易于编写的异步方法

  C#
中的 async 和 await 关键字都以异步编制程序的骨干。通过动用那多少个至关心重视要字,大家就足以在
.NET 轻松创制异步方法。

  示例:

 1         /// <summary>
 2         /// 异步访问 Web 
 3         /// </summary>
 4         /// <returns></returns>
 5         /// <remarks>
 6         /// 方法签名的 3 要素:
 7         ///     ① async 修饰符
 8         ///     ② 返回类型 Task 或 Task<TResult>:这里的 Task<int> 表示 return 语句返回 int 类型
 9         ///     ③ 方法名以 Async 结尾
10         /// </remarks>
11         async Task<int> AccessTheWebAsync()
12         {
13             //记得 using System.Net.Http 哦
14             var client = new HttpClient();
15 
16             //执行异步方法 GetStringAsync
17             Task<string> getStringTask = client.GetStringAsync("http://www.google.com.hk/");
18 
19             //假设在这里执行一些非异步的操作
20             Do();
21 
22             //等待操作挂起方法 AccessTheWebAsync
23             //直到 getStringTask 完成,AccessTheWebAsync 方法才会继续执行
24             //同时,控制将返回到 AccessTheWebAsync 方法的调用方
25             //直到 getStringTask 完成后,将在这里恢复控制。
26             //然后从 getStringTask 拿到字符串结果
27             string urlContents = await getStringTask;
28 
29             //返回字符串的长度(int 类型)
30             return urlContents.Length;
31         }

 

  假设 AccessTheWebAsync 在调用 GetStringAsync() 时未有其余操作(如:代码中的
Do()),你能够用那样的章程来简化代码。

string urlContents = await client.GetStringAsync("http://www.google.com.hk/");

  

  简单总括:

  (①)方法签名包涵多少个 async 修饰符。

  (2)依据约定,异步方法的称谓必要以“Async”后缀为末段。

  (3)3种回到类型:

    ① Task<TResult>:返回
TResult 类型。

    2Task:未有重返值,即重临值为 void。

    ③void:只适用于异步事件处理程序。

  (四)方法一般包蕴至少一个await
表明式,该表达式标记贰个点,大家能够变成悬挂点,在该点上,直到等待的异步操作完毕,之后的措施展才能能继续执行。
与此同时,该办法将挂起,并将控制权再次来到到点子的调用方。

  

  需求利用异步方法的话,大家间接在系统之中选用所提供的主要字
async 和 await 就足以了,剩余的其余业务,就留给编写翻译器吧。 

 

Task<TResult>

Task<TResult>是Task的泛型版本,那四个里头的最大不相同是Task<TResult>能够有3个重回值,看一下代码应该一目领悟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
 
namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("主线程开始");
 
            Task<string> task = Task<string>.Run(() => { Thread.Sleep(1000); return Thread.CurrentThread.ManagedThreadId.ToString(); });
            Console.WriteLine(task.Result);
 
            Console.WriteLine("主线程结束");
        }
    }
}
 
Task<TResult>的使用

Task<TResult>的实例对象有1个Result属性,当在Main方法中调用task.Result的时候,将拭目以俟task执行实现并获得重回值,那里的效用跟调用task.Wait()是同等的,只是多了二个重临值。

三、更便于编写的异步方法

  C#
中的 async 和 await 关键字都以异步编制程序的主干。通过利用那五个基本点字,你能够选拔.NET framework 或 Windows
运维时中的财富轻松成立异步方法(差不离与创立同步方法壹致自在)。

  下边的示范演示了一种采用async 和 await 定义的异步方法。

 1         /// <summary>
 2         /// 异步访问 Web 
 3         /// </summary>
 4         /// <returns></returns>
 5         /// <remarks>
 6         /// 方法签名的 3 要素:
 7         ///     ① async 修饰符
 8         ///     ② 返回类型 Task 或 Task<TResult>:这里的 Task<int> 表示 return 语句返回 int 类型
 9         ///     ③ 方法名以 Async 结尾
10         /// </remarks>
11         async Task<int> AccessTheWebAsync()
12         {
13             //记得 using System.Net.Http 哦
14             var client = new HttpClient();
15 
16             //执行异步方法 GetStringAsync
17             Task<string> getStringTask = client.GetStringAsync("http://www.google.com.hk/");
18 
19             //假设在这里执行一些非异步的操作
20             DoIndependentWork();
21 
22             //等待操作挂起方法 AccessTheWebAsync
23             //直到 getStringTask 完成,AccessTheWebAsync 方法才会继续执行
24             //同时,控制将返回到 AccessTheWebAsync 方法的调用方
25             //直到 getStringTask 完成后,将在这里恢复控制。
26             //然后从 getStringTask 拿到字符串结果
27             string urlContents = await getStringTask;
28 
29             //返回字符串的长度(int 类型)
30             return urlContents.Length;
31         }

 

  假如 AccessTheWebAsync 在调用 GetStringAsync
时未有任何操作,你能够用这么的秘籍来简化代码。

string urlContents = await client.GetStringAsync("http://www.google.com.hk/");

  

  依照以上代码举办简单总结:

  (1)方法签名包涵2个 async 修饰符。

  (二)依据预约,异步方法的称呼以“Async”后缀为末段。

  (3)再次回到类型为下列项目之一:

    一 假若你的艺术有操作数为
TResult 类型的回到语句,则为 Task<TResult>。

    贰 纵然你的法子没有回来语句或具备未有操作数的归来语句,则为 Task。

    三 要是您编写的是异步事件处理程序,则为
void。

  (四)方法1般包罗至少二个await
表明式,该表明式标记三个点,在该点上,直到等待的异步操作完结章程才能继续。 同时,将艺术挂起,并且控制权将回来到点子的调用方。

  在异步方法中,可使用提供的机要字和类型来提示供给做到的操作,且编译器会成功其余操作。 

 

4、异步方法的控制流(主题)

  异步编制程序中最要紧却不易懂的是控制流,即分化措施间的切换。以后,请用壹颗感恩的心来调查下图。

金沙注册送58 3

  步骤解析:

  1 事件处理程序调用并等候 AccessTheWebAsync() 异步方法。

  贰 AccessTheWebAsync
创制 HttpClient 对象并调用它的 GetStringAsync 异步方法来下载网址内容。

金沙注册送58 ,  叁假使 GetStringAsync 中发出了某种情形,该景况挂起了它的进度。大概必须等待网址下载或局地别的阻塞的位移。为防止阻塞财富,GetStringAsync() 会将控制权出让给其调用方 AccessTheWebAsync。GetStringAsync 重临 Task,在这之中 TResult
为字符串,并且 AccessTheWebAsync 将任务分配给 getStringTask 变量。该职责表示调用 GetStringAsync 的正在进展的经过,其中承诺当工作到位时发生实际字符串值。

  四 由于未有等待 getStringTask,因而,AccessTheWebAsync 能够继续执行不依靠于 GetStringAsync 得出最后结果的其它任务。该义务由对同步方法 DoIndependentWork 的调用表示。

  5 DoIndependentWork 是成就其行事并再次来到其调用方的协同方法。

  陆 AccessTheWebAsync 已做到工作,能够不受 getStringTask 的结果影响。 接下来,AccessTheWebAsync 需求计算并回到该下载字符串的长度,但该措施仅在享有字符串时才能揣测该值。因而,AccessTheWebAsync 使用3个 await
运算符来挂起其速度,并把控制权交给调用 AccessTheWebAsync 的艺术。AccessTheWebAsync 将 Task<int> 重回至调用方。 该任务表示对发生下载字符串长度的平头结果的一个承诺。

  【备注】如若 GetStringAsync(即 getStringTask)在 AccessTheWebAsync 等待前完结,则控制权会保留在 AccessTheWebAsync 中。 假若异步调用进度(getStringTask) 已成功,并且 AccessTheWebSync
不必等待最终结果,则挂起接下来再次回到到 AccessTheWebAsync,但那会招致基金的浪费。

  在调用方内部(借使那是3个事件处理程序),处理方式将继续。在等候结果前,调用方可以开始展览不重视于 AccessTheWebAsync 结果的任何工作,不然就需静观其变片刻。事件处理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。

  7 GetStringAsync 完毕并生成叁个字符串结果。 字符串结果不是透过你预期的诀窍调用 GetStringAsync 所再次来到的。(请牢记,此方法已在步骤 三中回到2个任务。)相反,字符串结果存款和储蓄在表示实现章程 getStringTask 的任务中。 await
运算符从 getStringTask 中搜寻结果。赋值语句将寻找到的结果赋给 urlContents。

  8 当 AccessTheWebAsync 具有字符串结果时,该办法可以测算字符串长度。然后,AccessTheWebAsync 工作也将形成,并且等待事件处理程序可继续应用。 

 

  你能够品味考虑一下协同行为和异步行为之间的差别。当其工作形成时(第陆 步)会回到1个一起方法,但当其工作挂起时(第 三 步和第 6步),异步方法会重返1个职分值。在异步方法末了形成其工作时,职分会标记为已做到,而结果(即便有)将积存在职务中。

 

async/await 特性

因而前面包车型客车铺垫,终于迎来了那篇小说的支柱async/await,依旧先经过代码来感受一下那三个特点的行使。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
 
namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("-------主线程启动-------");
            Task<int> task = GetLengthAsync();
            Console.WriteLine("Main方法做其他事情");
            Console.WriteLine("Task返回的值" + task.Result);
            Console.WriteLine("-------主线程结束-------");
        }
 
        static async Task<int> GetLengthAsync()
        {
            Console.WriteLine("GetLengthAsync Start"); 
            string str = await GetStringAsync();
            Console.WriteLine("GetLengthAsync End");
            return str.Length;
        }
 
        static Task<string> GetStringAsync()
        {
            return Task<string>.Run(() => { Thread.Sleep(2000); return "finished"; });
        }
    }
}
 
async/await 用法

先是来看一下async关键字。async用来修饰方法,表明那一个主意是异步的,证明的形式的回到类型必须为:void或Task或Task<TResult>。重返类型为Task的异步方法中无需选拔return重回值,而回到类型为Task<TResult>的异步方法中必须运用return重返2个TResult的值,如上述德姆o中的异步方法重回二个int。

再来看一下await关键字。await必须用来修饰Task或Task<TResult>,而且不得不出现在曾经用async关键字修饰的异步方法中。

普通状态下,async/await必须成对出现才有意义,要是三个艺术注脚为async,但却尚无选拔await关键字,则这么些方法在进行的时候就被当做同步方法,那时编写翻译器也会抛出警示提示async修饰的不2秘诀中绝非利用await,将被看成联合进行方法运用。明白了根本字async\await的风味后,我们来看一下上述德姆o在控制台会输入什么吗。

金沙注册送58 4

输出的结果已经很令人惊讶地报告大家全体实施流程了。GetLengthAsync异步方法刚开首是联合执行的,所以”GetLengthAsync
Start”字符串会被打字与印刷出来,直到遇见第1个await关键字,真正的异步职责GetStringAsync开头施行,await相当于起到五个标记/唤醒点的法力,同时将控制权放回给Main方法,”Main方法做任何事情”字符串会被打字与印刷出来。之后由于Main方法供给拜访到task.Result,所以就会等待异步方法GetLengthAsync的履行,而GetLengthAsync又等待GetStringAsync的履行,一旦GetStringAsync执行实现,就会回到await
GetStringAsync这几个点上进行往下执行,那时”GetLengthAsync
End”字符串就会被打字与印刷出来。

本来,大家也得以利用上面包车型客车不二秘籍成功地点控制台的出口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
 
namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("-------主线程启动-------");
            Task<int> task = GetLengthAsync();
            Console.WriteLine("Main方法做其他事情");
            Console.WriteLine("Task返回的值" + task.Result);
            Console.WriteLine("-------主线程结束-------");
        }
 
        static Task<int> GetLengthAsync()
        {
            Console.WriteLine("GetLengthAsync Start");
            Task<int> task = Task<int>.Run(() => { string str = GetStringAsync().Result;
                Console.WriteLine("GetLengthAsync End");
                return str.Length; });          
            return task;
        }
 
        static Task<string> GetStringAsync()
        {
            return Task<string>.Run(() => { Thread.Sleep(2000); return "finished"; });
        }
    }
}
 
不使用async\await

对照三种艺术,是否async\await关键字的法则其实正是通过运用1个线程实现异步调用吗?答案是或不是定的。async关键字标明能够在格局内部使用await关键字,方法在实践到await前都是同台实施的,运转到await处就会挂起,并重返到Main方法中,直到await标记的Task执行达成,才提示回到await点上,继续向下实施。更长远点的介绍能够查看小说最终的参考文献。

四、异步方法的控制流(主旨)

  异步编制程序中最需弄清的是控制流,即什么从一个格局移动到另2个形式,
请用一颗感恩的心来观看下图。

  步骤解析:

  1 事件处理程序调用并等候 AccessTheWebAsync 异步方法。

  贰 AccessTheWebAsync
创立 HttpClient 对象并调用它的 GetStringAsync 异步方法来下载网址内容。

  叁借使 GetStringAsync 中发出了某种情形,该情形挂起了它的历程。或者必须等待网址下载或局地别的阻塞的移动。为防止阻塞财富,GetStringAsync 会将控制权出让给其调用方 AccessTheWebAsync。GetStringAsync 再次来到 Task,个中 TResult
为字符串,并且 AccessTheWebAsync 将职务分配给 getStringTask 变量。该职务表示调用 GetStringAsync 的正在展开的长河,个中承诺当工作到位时爆发实际字符串值。

  4 由于未有等待 getStringTask,由此,AccessTheWebAsync 能够继续执行不借助于于 GetStringAsync 得出最后结果的别的职分。该职责由对同步方法 DoIndependentWork 的调用表示。

  五 DoIndependentWork 是到位其行事并重回其调用方的联手方法。

  陆 AccessTheWebAsync 已做到工作,能够不受 getStringTask 的结果影响。 接下来,AccessTheWebAsync 须要计算并回到该下载字符串的长度,但该措施仅在有着字符串时才能猜测该值。由此,AccessTheWebAsync 使用八个 await
运算符来挂起其速度,并把控制权交给调用 AccessTheWebAsync 的法子。AccessTheWebAsync 将 Task<int> 再次回到至调用方。 该职务表示对产生下载字符串长度的平头结果的二个承诺。

  【备注】要是 GetStringAsync(即 getStringTask)在 AccessTheWebAsync 等待前实现,则控制权会保留在 AccessTheWebAsync 中。 如若异步调用进度(getStringTask) 已做到,并且 AccessTheWebSync
不必等待最终结果,则挂起接下来再次回到到 AccessTheWebAsync,但那会招致资金的荒废。

  在调用方内部(假如那是一个事件处理程序),处理模式将一而再。在等候结果前,调用方能够举行不依靠于 AccessTheWebAsync 结果的任何干活,不然就需等候片刻。事件处理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。

  柒 GetStringAsync 完毕并生成多少个字符串结果。 字符串结果不是经过你预期的不二等秘书籍调用 GetStringAsync 所重返的。(请牢记,此格局已在步骤 3中回到1个任务。)相反,字符串结果存款和储蓄在代表实现措施 getStringTask 的职务中。 await
运算符从 getStringTask 中追寻结果。赋值语句将追寻到的结果赋给 urlContents。

  8 当 AccessTheWebAsync 具有字符串结果时,该方法能够测算字符串长度。然后,AccessTheWebAsync 工作也将形成,并且等待事件处理程序可继承采纳。 

 

  你能够品尝思考一下壹同行为和异步行为之间的差距。当其工作形成时(第陆 步)会回去3个一并方法,但当其行事挂起时(第 3 步和第 陆步),异步方法会重回1个职分值。在异步方法最后完成其行事时,任务会标记为已形成,而结果(假诺有)将积存在职分中。

 

伍、异步中的线程

  异步方法目的在于成为非阻塞操作。异步方法中的
await 表明式在等候的职务执行的还要不会卡住当前线程。相反,await
表明式在继续执行时方法的别的部分并将控制权再次来到到异步方法的调用方。

  async 和 await
关键字不会造成创制其余线程。因为异步方法不会在其本身线程上运营,因而它不须求八线程。唯有当方法处于活动状态时,该措施将在当前联合署名上下文中运作并采取线程上的小时。能够运用 Task.Run 将占据多量CPU
的工作移到后台线程,但是后台线程不会协理正在等候结果的进程变为可用状态。

  对于异步编制程序而言,该基于异步的情势优于大概各样用例中的现有措施。具体而言,此格局比 BackgroundWorker 更适用于
IO 绑定的操作,因为此代码更不难且无需防患当先争用规范。结合 Task.Run()
使用时,异步编制程序比 BackgroundWorker 更适用于 CPU
绑定的操作,因为异步编制程序将运营代码的调和细节与 Task.Run 传输至线程池的劳作分别开来。

 

async/await 实际应用

微软已经对部分基础类库的方法提供了异步实现,接下去将促成一个例证来介绍一下async/await的其实使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
 
namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("开始获取博客园首页字符数量");
            Task<int> task1 = CountCharsAsync("http://www.cnblogs.com");
            Console.WriteLine("开始获取百度首页字符数量");
            Task<int> task2 = CountCharsAsync("http://www.baidu.com");
 
            Console.WriteLine("Main方法中做其他事情");
 
            Console.WriteLine("博客园:" + task1.Result);
            Console.WriteLine("百度:" + task2.Result);
        }
 
        static async Task<int> CountCharsAsync(string url)
        {
            WebClient wc = new WebClient();
            string result = await wc.DownloadStringTaskAsync(new Uri(url));
            return result.Length;
        }
    }
}

五、线程

  异步方法目的在于成为非阻塞操作。异步方法中的
await 表明式在等候的任务正在运作时不会阻塞当前线程。相反,表达式在持续时登记形式的其他部分并将控制权重临到异步方法的调用方。

  async
和 await 关键字不会促成创设别的线程。因为异步方法不会在其本身线程上运转,因而它不要求102线程。唯有当方法处于活动状态时,该办法将在时下壹并上下文中运作并动用线程上的岁月。能够动用 Task.Run 将占用大量CPU
的做事移到后台线程,可是后台线程不会赞助正在等候结果的历程变为可用状态。

  对于异步编制程序而言,该基于异步的艺术优于差不离各类用例中的现有措施。具体而言,此办法比 BackgroundWorker 更适用于
IO 绑定的操作,因为此代码更不难且无需提防超过争用口径。结合 Task.Run 使用时,异步编制程序比 BackgroundWorker 更适用于
CPU 绑定的操作,因为异步编制程序将运转代码的协调细节与 Task.Run 传输至线程池的劳作分别开来。

 

六、async 和 await 修饰符

  当你选拔 async 修饰符钦点该格局为异步方法时:

  • 可以使用 await 来钦点悬挂点。await
    运算符会告诉编写翻译器,异步方法唯有直到等待的异步进度执行到位,才能继承通过该点往下实施。同时,控制权将回到至异步方法的调用方。await
    表达式中异步方法在挂起后,要是该办法还并未有履行到位并退出,finally 块中的将不会执行。

  • 标记的异步方法本人可以经过调用它的主意开始展览等待。异步方法中1般包罗三个或多个await 运算符,当然,3个 await
    表明式都不设有也不会导致编写翻译器错误,可是编写翻译器会生出警示,该办法在履行的时候仍旧会遵从同步方法来举行,async
    其实只是1个标识的职能而已,告诉编写翻译器他“应该”是1个异步方法。

 

六、async 和 await

  假如经过行使 async 修饰符钦赐某种格局为异步方法,则会并发上面二种现象。

  • 标志的异步方法能够动用 await 来钦定悬挂点。await
    运算符文告编译器异步方法唯有直到等待的异步进度一呵而就才能再而三透过该点。同时,控制权将赶回至异步方法的调用方。

    await
    表明式中异步方法的挂起不可能使该办法退出,并且 finally 块不会运行。

  • 标记的异步方法自身能够由此调用它的点子等待。

  异步方法1般包含await 运算符的3个或四个匹配项,但缺少 await
表明式不会促成编译器错误。要是异步方法未采用await
运算符标记悬挂点,则该方法将作为共同方法执行,不管异步修饰符怎么着。编写翻译器将为此类措施公布三个告诫。

 

7、重临类型和参数消息

  在编排异步方法时,大家多方会动用
Task 和 Task<TResult> 作为重临类型。

 

  示例:

 1         static async Task<Guid> Method1Async()  //Task<Guid>
 2         {
 3             var result = Guid.NewGuid();
 4 
 5             await Task.Delay(1);
 6 
 7             //这里返回一个 Guid 的类型
 8             return result;
 9         }
10 
11         static async Task Method2Async()  //Task
12         {
13             //Do...
14 
15             await Task.Delay(1);
16 
17             //Do...
18 
19             //这里没有 return 语句
20         }

 1             //调用 Method1Async
 2             //方式一
 3             Task<Guid> t1 = Method1Async();
 4             Guid guid1 = t1.Result;
 5 
 6             //方式二
 7             Guid guid2 = await Method1Async();
 8 
 9             //调用 Method2Async
10             //方式一
11             Task t2 = Method2Async();
12             await t2;
13 
14             //方式二
15             await Method2Async();

  每一种再次回到的天职表示正在展开的行事。职分可包裹有关异步进度情状的消息,要是未成功,则最后会卷入来自进度的末尾结果,大概是由该进度引发的不行。

 

  【疑问】那么
void 重回类型是在怎么情况下才使用的吧?

  主要用来异步的事件处理程序,异步事件处理程序常常作为异步程序的初叶点。void
重回类型告诉了编写翻译器,无需对她开始展览等待,并且,对于 void
重临类型的措施,大家也无力回天对他展开至极的捕捉。

 

  异步方法不可见在参数中申明与行使
ref 和 out 关键字,可是异步方法可以调用包括这几个参数的点子。

 

7、重回类型和参数新闻

  在
.NET 中,异步方法①般重临 Task 或 Task<TResult>。在异步方法中,await
运算符应用于通过调用另1个异步方法再次回到的职分。

  即使方式包括 钦赐项目 TResult 的操作数的 return 语句,则将 Task<TResult> 钦点为回到类型。

  假如艺术不含任何
return 语句或含有不回去操作数的 return 语句,则将 Task 用作重临类型。

  上边包车型大巴示范演示如何申明并调用可回到 Task 或 Task<TResult>
的点子。

 1         static async Task<Guid> Method1Async()  //Task<Guid>
 2         {
 3             var result = Guid.NewGuid();
 4 
 5             await Task.Delay(1);
 6 
 7             //这里返回一个 Guid 的类型
 8             return result;
 9         }
10 
11         static async Task Method2Async()  //Task
12         {
13             //Do...
14 
15             await Task.Delay(1);
16 
17             //Do...
18 
19             //这里没有 return 语句
20         }

 1             //调用 Method1Async
 2             //方式一
 3             Task<Guid> t1 = Method1Async();
 4             Guid guid1 = t1.Result;
 5 
 6             //方式二
 7             Guid guid2 = await Method1Async();
 8 
 9             //调用 Method2Async
10             //方式一
11             Task t2 = Method2Async();
12             await t2;
13 
14             //方式二
15             await Method2Async();

  每个再次回到的职务表示正在开始展览的行事。任务可包裹有关异步进度情状的新闻,如若未得逞,则最终会卷入来自进度的末梢结出或进程引发的10分。

  异步方法还足以是负有 void 重回类型。该重临类型主要用来定义须要 void 重临类型的事件处理程序。异步事件处理程序经常作为异步程序的早先点。

  不恐怕等待具有 void 再次回到类型的异步方法,并且贰个void 重返值的调用方不大概捕获该办法引发的任何尤其。

  异步方法不可能表明C# 中的 ref 或 out 参数,但此措施可以调用具有此类参数的章程。

 

八、命名的预订

  根据预订,使用
async 的办法都应该以“Async”作为后缀,如:DownloadAsync() 。可是,假使某一约定中的事件、基类或接口有别的的花样约定,则能够忽略上述约定。例如,不该修改或重命名常用事件处理程序,如 btnOpen_Click。

 

八、命名的预约

  依照预订,将“Async”追加到持有 async 修饰符的法子名称。

  假使某1约定中的事件、基类或接口协定建议其余名目,则能够忽略此预约。例如,你不应重命名常用事件处理程序,例如 btnOpen_Click。

 

传送门 

  1. 走进异步编制程序的社会风气 – 早先接触
    async/await(推荐)

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

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

  4. 走进异步编制程序的社会风气 – 在 GUI
    中实践异步操作

 


【参考引用】微软官方文书档案图片

【参考】

 

传送门 

 


【参考引用】微软官方文档图片

 

 

] 利用 async await
的异步编制程序,.net利用async 利用 async await 的异步编制程序 【博主】反骨仔
【出处】 目录…

相关文章

网站地图xml地图