科学普及的异步形式async,的异步编制程序

以前切磋过c#的async和await关键字,幕后干了哪些,然则不明白为啥找不到相关资料了。以后再也钻探叁次,顺便记录下来,方便将来翻看。

初稿地址: 

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

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

【博主】反骨仔    【出处】   

基础知识

async
关键字标澳优个艺术,该方法再次来到值是五个Task、只怕Task<TResult>、void、包括GetAwaiter方法的品类。该办法一般包括多少个await表明式。该表明式标注1个点,将被有个别异步方法回跳到该点。并且,当前函数执行到该点,将马上回去控制权给调用方。

以上描述了async方法想干的作业,至于怎么达成,那里就不读书了。

 

选取 async & await 的异步编程

【博主】反骨仔    【出处】   

目录

  • 异步编制程序的简介
  • 异步进步响应能力
  • 科学普及的异步形式async,的异步编制程序。更便于编写的异步方法
  • 异步方法的控制流(核心)
  • 异步中的线程
  • async 和 await
    修饰符
  • 回来类型和参数音信
  • 取名的约定

 

个人见解

由此能够明白,async
和await关键字主要目的是为着控制异步线程的一块,让1个异步进程,表现得近乎一块进程同样。

诸如async
方法分n个职责去下载网页并举行拍卖:先await下载,然后随即回到调用方,之后的处理就由异步线程完结下载后调用。那时候调用方可以继续执行它的天职,但是,要是调用方立时就要求async的结果,那么应该就不得不等待,但是大多数动静:他临时不必要那些结果,那么就能够并行处理那么些代码。

看得出,并行性呈以往await 上,假设await
点和最终的多寡结果偏离越远,那么并行度就越高。假设await的点愈多,相信也会改正并行性。

资料展现,async 和await
关键字并不会创设线程,那是很主要的一些。
她俩只是创立了一个回来点,提需求急需他的线程使用。那么线程究竟是何人创造?注意await
表明式的结合,他须要一个Task,1个Task并不意味着一定要创造线程,也足以是另1个async方法,不过层层包装最里面包车型地铁艺术,很或许正是四个原生的Task,比如await
Task.Run(()=>Thread.Sleep(0));
,这一个真的发出线程的话语,就会依照后面那1个await点,每个回调。

从那点来看,async
方法,未必就是八个异步方法,他在语义上特别靠近“非阻塞”,
当蒙受阻塞操作,立即用await定点重回,至于别的更深一层的消除手段,它就不关切了。那是程序员供给关爱的,程序员需要用真的的创导线程代码,来达成异步操作(当然这一步可由库程序员落成)。

只顾async的多少个重临值类型,那意味着了分裂的使用情状。如若是void,表达客户端不关注数据同步难点,它只须求线程的控制权马上回去。可以用在ui
等场所,如若是Task,客户端也不关切数据,可是它希望能够支配异步线程,那恐怕是对职分执行种种有必然的须求。当然,最普遍的是Task<TResult>。

综上,async和await并不是为着多职责而安插的,假设追求高产出,应该在async函数内部用Task好好规划一番。在动用async
和await的时候,只须要遵从非阻塞的笔触去编写代码就可以了,至于幕后怎么处理就交付真正的二十四线程代码创制者吧。

一齐编制程序与异步编制程序

常备景况下,大家写的C#代码正是一道的,运转在同三个线程中,从程序的第三行代码到结尾一句代码顺序执行。而异步编制程序的主导是运用八线程,通过让分歧的线程执行不一样的职务,实现不一样代码的并行运行。

目录

  • 异步编制程序的简介
  • 异步提升响应能力
  • 更便于编写的异步方法
  • 异步方法的控制流(宗旨)
  • 线程
  • async 和 await
  • 回到类型和参数消息
  • 命名的预订

 

一 、异步编制程序的简介

  通过动用异步编制程序,你可防止止质量瓶颈并提升你的应用程序的完好响应能力。

  从
VS 二〇一一 初阶,新引入了1个简化的艺术,称为异步编制程序。大家在 >= .NET
4.5 中和 Windows 运营时中使用异步,编写翻译器它会拉扯了小编们下落了曾经举办的高难度异步代码编写的工作,但逻辑结构却看似于一块代码。因而,我们仅必要展开一小部分编制程序的行事就可以获取异步编制程序的有着优点。

 

示范代码

        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);
        }

前台线程与后台线程

至于多线程,早在.NET2.0一代,基础类库中就提供了Thread达成。暗中同意景况下,实例化二个Thread创立的是前台线程,只要有前台线程在运作,应用程序的经过就向来处于运行情况,以控制台应用程序为例,在Main方法中实例化三个Thread,这几个Main方法就会等待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
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("这是后台线程调用");
        }
    }
}

运作方面包车型大巴代码,能够看看DoRun2方法的打字与印刷音讯“那是后台线程调用”将不会被出示出来,因为应用程序执行完主线程和前台线程后,就自动退出了,全部的后台线程将被电动终止。那里后台线程设置了守候1.5s,假诺那么些后台线程比前台线程或主线程提前实施完成,对应的新闻“那是后台线程调用”将可以被成功打字与印刷出来。

① 、异步编制程序的简介

  通过利用异步编制程序,你能够免止品质瓶颈并加强应用程序的一体化响应能力。

  Visual
Studio 二〇一二 引入了二个简化的法子,异步编制程序,在 .NET Framework 4.5 和
Windows 运行时行使异步援救。编写翻译器可实施开发人士曾举行的高难度工作,且应用程序保留了二个近似于一块代码的逻辑结构。因而,您仅须求展开一小部分行事就可以收获异步编制程序的具备优点。

 

② 、异步升高响应能力

  异步对或然滋生短路的运动(如访问
Web 时),对
Web 财富的拜会有时过慢或延迟过高。若那种职责在一起进程中受阻,则全体应用程序必须等待响应实现。 在利用异步的进度中,我们的应用程序可继续执行不依赖Web
能源的别的工作,并会直接等候绿灯的职责顺遂达成。

  那是有个别特出的应用异步的应用场景,以及部分在
.NET >= 4.5 后新增的类库。

美高梅开户网址 1

  全部与用户界面相关的操作经常共享一个线程,所以选用异步对于利用 UI
线程的 App 来说是不行主要的。

  如若说你的 App
全数操作都以共同的,也正是说,当二个线程出现堵塞,别的线程都会见世堵塞,更严重的是,
App 会甘休响应。

美高梅开户网址 2

 

  使用异步方法时,App
将继承响应
UI。如:最大和最小化,但是意义照旧在后台执行(如:下载)。

 

并行编码方法

并行执行有多少个方法,第四个是创设n个Task,一起运维。难题是怎么处理await点。每种task写四个await点是可怜的,因为境遇第①个await就马上回去,而不会敞开全数任务并行执行。因而await不能够随便放。那么什么样为一组Task设定await点呢?能够通过Task.WhenAll
这几个法子,他会等待一组Task执行完成重临。

特定情景下,可以用Parallel.For
来打开一组任务,然而这一个类并不曾兑现async情势,也等于它会堵塞当前线程,所以须要用1个Task来包裹它。

看得出,非阻塞和互动不完全是三遍事。

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底层使用线程池

运营代码,可以看出DoRun1()方法每趟的Thread
Id都以例外的,而DoRun2()方法的Thread
Id是再次现身的。我们明白线程的创办和销毁是1个费用比较大的操作,Task.Run()每一次执行将不会应声成立三个新线程,而是到CL兰德酷威线程池查看是不是有空余的线程,有的话就取一个线程处理那些请求,处理完请求后再把线程放回线程池,那一个线程也不会马上裁撤,而是设置为空闲状态,可供线程池再次调度,从而收缩开销。

② 、异步提升响应能力

  异步对大概滋生短路的运动(例如应用程序访问
Web 时)至关主要。对
Web 财富的造访有时非常慢或会延迟。借使此类活动在一齐过程中受阻,则全体应用程序必须等待。 在异步进程中,应用程序可继续执行不重视Web 财富的其余工作,直至潜在阻塞的天职成功。

  下图浮现了异步编制程序进步响应能力的头名应用场景。包罗从
.NET Framework 4.5 和 Windows
运维时中列出的部分分包帮忙异步编制程序的措施的类。

  由于全数与用户界面相关的移动一般共享五个线程,由此,异步对走访
UI 线程的应用程序来说特别首要。 借使在二个体协会同应用程序中有其余的线程被堵塞了,那么富有线程都将被打断,再严重一点,你的应用程序将会停下响应。

  使用异步方法时,应用程序将持续响应
UI。例如,你能够调动窗口的分寸或最小化窗口;假若您不期望等待应用程序停止,则足以将其倒闭。

 

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

  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/");

  

  简单计算:

  (1)方法签名包罗七个 async 修饰符。

  (2)依据预定,异步方法的称谓供给以“Async”后缀为结尾。

  (3)3
种回到类型:

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

    ②
Task:没有再次回到值,即再次来到值为 void。

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

  (4)方法一般包罗至少一个await
表明式,该表明式标记三个点,大家得以变成悬挂点,在该点上,直到等待的异步操作达成,之后的章程才能继续执行。
与此同时,该情势将挂起,并将控制权重回到方法的调用方。

  

  须求采取异步方法的话,大家直接在系统里面选用所提供的要紧字
async 和 await 就足以了,剩余的别的业务,就留给编写翻译器吧。 

 

Task<TResult>

Task<TResult>是Task的泛型版本,那五个以内的最大不一样是Task<TResult>可以有三个再次来到值,看一下代码应该一目明白:

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>的实例对象有2个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)方法签名包蕴一个 async 修饰符。

  (2)依据约定,异步方法的称号以“Async”后缀为终极。

  (3)重返类型为下列项目之一:

    ① 倘诺您的章程有操作数为
TResult 类型的回来语句,则为 Task<TResult>。

    ② 假使您的格局没有回去语句或富有没有操作数的回到语句,则为 Task。

    ③ 即使您编写的是异步事件处理程序,则为
void。

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

  在异步方法中,可采用提供的主要字和类型来提醒要求形成的操作,且编写翻译器会实现别的操作。 

 

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

  异步编程中最要紧却不易懂的是控制流,即差别格局间的切换。现在,请用一颗感恩的心来观看下图。

美高梅开户网址 3美高梅开户网址

  步骤解析:

  ① 事件处理程序调用并伺机 AccessTheWebAsync() 异步方法。

  ② AccessTheWebAsync
创造 HttpClient 对象并调用它的 GetStringAsync 异步方法来下载网站内容。

  ③
若是 GetStringAsync 中发出了某种情形,该景况挂起了它的长河。大概必须等待网站下载或一些别的阻塞的移动。为制止阻塞财富,GetStringAsync() 会将控制权出让给其调用方 AccessTheWebAsync。GetStringAsync 重回 Task,其中 TResult
为字符串,并且 AccessTheWebAsync 将职分分配给 getStringTask 变量。该职务表示调用 GetStringAsync 的正在拓展的进度,当中承诺当工作成功时发出实际字符串值。

  ④ 由于尚未等待 getStringTask,因而,AccessTheWebAsync 能够继续执行不依靠于 GetStringAsync 得出最后结果的别样任务。该职分由对联合方法 DoIndependentWork 的调用表示。

  ⑤ DoIndependentWork 是形成其行事并赶回其调用方的一道方法。

  ⑥ AccessTheWebAsync 已做到工作,可以不受 getStringTask 的结果影响。 接下来,AccessTheWebAsync 须要计算并再次来到该下载字符串的尺寸,但该措施仅在装有字符串时才能推测该值。因而,AccessTheWebAsync 使用2个 await
运算符来挂起其速度,并把控制权交给调用 AccessTheWebAsync 的法门。AccessTheWebAsync 将 Task<int> 重临至调用方。 该任务表示对发出下载字符串长度的平头结果的3个承诺。

  【备注】假如 GetStringAsync(即 getStringTask)在 AccessTheWebAsync 等待前成功,则控制权会保留在 AccessTheWebAsync 中。 假诺异步调用进程(getStringTask) 已做到,并且 AccessTheWebSync
不必等待末了结果,则挂起接下来回来到 AccessTheWebAsync,但那会招致资金的荒废。

  在调用方内部(尽管那是3个事件处理程序),处理形式将继承。在伺机结果前,调用方能够进行不注重于 AccessTheWebAsync 结果的别样干活,不然就需拭目以俟片刻。事件处理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。

  ⑦ GetStringAsync 达成并生成三个字符串结果。 字符串结果不是透过你预期的法子调用 GetStringAsync 所再次来到的。(请牢记,此方式已在步骤 3
中回到1个职务。)相反,字符串结果存款和储蓄在表示完毕措施 getStringTask 的天职业中学。 await
运算符从 getStringTask 中寻觅结果。赋值语句将追寻到的结果赋给 urlContents。

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

 

  你能够品尝思考一下联机行为和异步行为之间的异样。当其工作成就时(第④ 步)会回去2个共同方法,但当其行事挂起时(第 3 步和第 6
步),异步方法会重返三个任务值。在异步方法最后成功其行事时,义务会标记为已形成,而结果(如若有)将积存在职务中。

 

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再次回到二个TResult的值,如上述德姆o中的异步方法再次来到二个int。

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

一般说来状态下,async/await必须成对出现才有意义,假设1个措施证明为async,但却尚未采用await关键字,则这么些方法在实施的时候就被当作同步方法,那时编写翻译器也会抛出警示提醒async修饰的章程中没有选择await,将被看成联合实行方法应用。驾驭了关键字async\await的风味后,大家来看一下上述德姆o在支配台会输入什么呢。

美高梅开户网址 4

出口的结果早就很显然地告知大家一切实施流程了。GetLengthAsync异步方法刚初叶是同台实施的,所以”GetLengthAsync
Start”字符串会被打字与印刷出来,直到遭受第二个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关键字的规律其实就是经过行使二个线程实现异步调用吗?答案是或不是认的。async关键字标明能够在艺术内部采取await关键字,方法在实施到await前都以一路施行的,运转到await处就会挂起,并回到到Main方法中,直到await标记的Task执行达成,才提示回到await点上,继续向下执行。更深切点的牵线可以查阅小说最终的参考文献。

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

  异步编制程序中最需弄清的是控制流,即什么从二个艺术移动到另多个艺术,
请用一颗感恩的心来考察下图。

  步骤解析:

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

  ② AccessTheWebAsync
创设 HttpClient 对象并调用它的 GetStringAsync 异步方法来下载网站内容。

  ③
即使 GetStringAsync 中产生了某种意况,该情状挂起了它的历程。只怕必须等待网站下载或局地别样阻塞的移动。为制止阻塞财富,GetStringAsync 会将控制权出让给其调用方 AccessTheWebAsync。GetStringAsync 重返 Task,在那之中 TResult
为字符串,并且 AccessTheWebAsync 将职务分配给 getStringTask 变量。该职分表示调用 GetStringAsync 的正在展开的长河,个中承诺当工作到位时发生实际字符串值。

  ④ 由于没有等待 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
中回到二个任务。)相反,字符串结果存款和储蓄在代表完结措施 getStringTask 的职分中。 await
运算符从 getStringTask 中查找结果。赋值语句将追寻到的结果赋给 urlContents。

  ⑧ 当 AccessTheWebAsync 具有字符串结果时,该格局可以测算字符串长度。然后,AccessTheWebAsync 工作也将形成,并且等待事件处理程序可继续选取。 

 

  你能够尝尝思考一下一同行为和异步行为之间的差异。当其工作形成时(第伍 步)会回来三个三只方法,但当其工作挂起时(第 3 步和第 6
步),异步方法会重回二个职责值。在异步方法最后成功其工作时,任务会标记为已到位,而结果(假设有)将积存在职分中。

 

5、异步中的线程

  异步方法意在成为非阻塞操作。异步方法中的
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 关键字不会招致创立其余线程。因为异步方法不会在其自小编线程上运维,因而它不必要四线程。只有当方法处于活动状态时,该方法将在近来共同上下文中运作并应用线程上的岁月。能够应用 Task.Run 将占据多量CPU
的行事移到后台线程,不过后台线程不会帮助正在等待结果的长河变为可用状态。

  对于异步编程而言,该基于异步的艺术优于差不多各样用例中的现有措施。具体而言,此方法比 BackgroundWorker 更适用于
IO 绑定的操作,因为此代码更简短且无需防备超越争用标准化。结合 Task.Run 使用时,异步编制程序比 BackgroundWorker 更适用于
CPU 绑定的操作,因为异步编制程序将运行代码的和谐细节与 Task.Run 传输至线程池的干活分别开来。

 

六、async 和 await 修饰符

  当您使用 async 修饰符钦赐该措施为异步方法时:

  • 能够选拔 await 来钦命悬挂点。await
    运算符会告诉编写翻译器,异步方法唯有直到等待的异步进度进行到位,才能持续透过该点往下实施。同时,控制权将赶回至异步方法的调用方。await
    表达式中异步方法在挂起后,假若该格局还没有实行到位并脱离,finally 块中的将不会履行。

  • 标记的异步方法自身能够由此调用它的点子进行等待。异步方法中一般包蕴3个或多个await 运算符,当然,多少个 await
    表达式都不存在也不会促成编写翻译器错误,不过编写翻译器会生出警示,该形式在推行的时候依旧会遵照同步方法来实施,async
    其实只是二个标识的意义而已,告诉编写翻译器他“应该”是二个异步方法。

 

六、async 和 await

  假使由此利用 async 修饰符钦定某种格局为异步方法,则会油可是生上面三种现象。

  • 标志的异步方法能够采纳 await 来钦命悬挂点。await
    运算符公告编写翻译器异步方法唯有直到等待的异步进度实现才能继续透过该点。同时,控制权将赶回至异步方法的调用方。

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

  • 标志的异步方法自个儿能够经过调用它的艺术等待。

  异步方法一般包罗await 运算符的叁个或三个匹配项,但缺少 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 关键字,可是异步方法能够调用包蕴那几个参数的章程。

 

柒 、再次来到类型和参数新闻

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

  要是格局包罗 钦赐项目 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();

  种种重返的天职表示正在拓展的行事。义务可包裹有关异步进度境况的音讯,尽管未成功,则末了会卷入来自进度的末尾结果或进度引发的相当。

  异步方法还足以是装有 void 重返类型。该重返类型首要用来定义要求 void 重返类型的事件处理程序。异步事件处理程序常常作为异步程序的初始点。

  不能够等待具有 void 重返类型的异步方法,并且1个void 重返值的调用方不可能捕获该方法引发的此外很是。

  异步方法不也许评释C# 中的 ref 或 out 参数,但此办法能够调用具有此类参数的艺术。

 

⑧ 、命名的约定

  依照约定,使用
async 的主意都应有以“Async”作为后缀,如:DownloadAsync() 。可是,假若某一约定中的事件、基类或接口有任何的款式约定,则足以忽略上述约定。例如,不应有修改或重命名常用事件处理程序,如 btnOpen_Click。

 

八 、命名的约定

  依据约定,将“Async”追加到具有 async 修饰符的点子名称。

  如若某一约定中的事件、基类或接口协定建议任何名目,则足以忽略此预订。例如,你不应重命名常用事件处理程序,例如 btnOpen_Click。

 

传送门 

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

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

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

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

 


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

【参考】

 

传送门 

 


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

 

 

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

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图