【www.30064.com】的异步编制程序,关于异步执行

以前研讨过c#的async和await关键字,幕后干了怎么着,然则不知情为啥找不到相关资料了。以后再也研究二次,顺便记录下来,方便今后翻看。

原稿地址: 

[.NET]【www.30064.com】的异步编制程序,关于异步执行。 利用 async & await 的异步编制程序,.net利用async

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

【博主】反骨仔    【出处】   

基础知识

async
关键字标圣元(Synutra)个主意,该方法重返值是2个Task、或然Task<TResult>、void、包蕴GetAwaiter方法的品种。该办法一般包罗三个await说明式。该表达式标注1个点,将被有个别异步方法回跳到该点。并且,当前函数执行到该点,将随即回去控制权给调用方。

如上描述了async方法想干的业务,至于何以落到实处,那里就不读书了。

 

选取 async & await 的异步编制程序

【博主】反骨仔    【出处】   

目录

  • 异步编制程序的简介
  • 异步进步响应能力
  • 更易于编写的异步方法
  • 异步方法的控制流(核心)
  • 异步中的线程
  • async 和 await
    修饰符
  • 重临类型和参数新闻
  • 取名的预订

 

个人见解

透过能够明白,async
和await关键字首要指标是为着控制异步线程的联合署名,让三个异步进程,表现得就像一块进度一样。

比如async
方法分n个职责去下载网页并举行拍卖:先await下载,然后马上回到调用方,之后的拍卖就由异步线程完成下载后调用。那时候调用方能够继续执行它的任务,不过,假如调用方登时就须要async的结果,那么相应就只可以等待,不过大多数情状:他一时半刻不须求那些结果,那么就足以并行处理这个代码。

可知,并行性彰显在await 上,借使await
点和最终的数量结果偏离越远,那么并行度就越高。假若await的点越来越多,相信也会创新并行性。

材质展示,async 和await
关键字并不会创造线程,那是很重点的有些。
他们只是创制了3个再次回到点,提供给急需她的线程使用。那么线程终归是何人成立?注意await
表达式的咬合,他索要1个Task,一个Task并不意味一定要开创线程,也得以是另贰个async方法,不过层层包装最中间的方法,非常大概正是二个原生的Task,比如await
Task.Run(()=>Thread.Sleep(0));
,那个实在发生线程的口舌,就会基于后面这些await点,每种回调。

从那一点来看,async
方法,未必就是三个异步方法,他在语义上特别接近“非阻塞”,
当境遇阻塞操作,立即用await定点再次来到,至于其余更深一层的解决手段,它就不关切了。那是程序员需求关切的,程序员必要用真的的创办线程代码,来形成异步操作(当然这一步可由库程序员完结)。

注意async的多少个重回值类型,那代表了分歧的应用意况。借使是void,表明客户端不爱戴数据同步难题,它只供给线程的控制权立时回到。能够用在ui
等场面,假诺是Task,客户端也不吝惜数据,可是它愿意可以控制异步线程,那恐怕是对职分履行顺序有早晚的渴求。当然,最常见的是Task<TResult>。

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

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

平日状态下,大家写的C#代码就是一块的,运维在同3个线程中,从程序的率先行代码到结尾一句代码顺序执行。而异步编制程序的基本是使用八线程,通过让分化的线程执行不一的天职,完成不相同代码的交互运营。

目录

  • 异步编制程序的简介
  • 异步升高响应能力
  • 更便于编写的异步方法
  • 异步方法的控制流(主题)
  • 线程
  • async 和 await
  • 回去类型和参数音讯
  • 取名的预订

 

一 、异步编制程序的简介

  通过选用异步编制程序,你能够制止质量瓶颈并增强你的应用程序的欧洲经济共同体响应能力。

  从
VS 二零一三 发轫,新引入了三个简化的方法,称为异步编制程序。大家在 >= .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达成。暗中同意景况下,实例化3个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 2011 引入了1个简化的办法,异步编制程序,在 .NET Framework 4.5 和
Windows 运转时使用异步帮助。编写翻译器可举行开发人士曾举办的高难度工作,且应用程序保留了1个近乎于一块代码的逻辑结构。因而,您仅必要展开一小部分干活就足以获取异步编程的装有优点。

 

② 、异步升高响应能力

  异步对大概引起短路的位移(如访问
Web 时),对
Web 能源的走访有时过慢或延迟过高。若那种义务在共同进度中受阻,则整体应用程序必须等待响应实现。 在应用异步的经过中,大家的应用程序可继续执行不注重Web
能源的任何干活,并会直接等候绿灯的职务顺遂达成。

  这是一对卓绝群伦的应用异步的应用场景,以及一些在
.NET >= 4.5 后新增的类库。

www.30064.com 1

  所有与用户界面相关的操作经常共享二个线程,所以接纳异步对于利用 UI
线程的 App 来说是充裕首要的。

  即便说你的 App
全部操作都以一路的,约等于说,当多个线程出现堵塞,别的线程都会现身堵塞,更要紧的是,
App 会结束响应。

www.30064.com 2

 

  使用异步方法时,App
将一而再响应
UI。如:最大和最小化,然而意义依旧在后台执行(如:下载)。

 

并行编码方法

并行执行有多少个点子,第贰个是创设n个Task,一起运营。难点是怎么处理await点。各个task写一个await点是那些的,因为蒙受第二个await就及时回去,而不会敞开全体职分并行执行。由此await不可能随便放。那么什么样为一组Task设定await点呢?能够通过Task.WhenAll
那个方法,他会等待一组Task执行达成重回。

特定情景下,能够用Parallel.For
来打开一组职分,可是这几个类并不曾落到实处async形式,也便是它会卡住当前线程,所以需求用多个Task来包裹它。

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

Task

.NET
4.0生产了新一代的多线程模型Task。async/await本性是与Task紧凑有关的,所以在摸底async/await前必须尽量领悟Task的选取。那里将以3个不难易行的德姆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每一遍实例化都会成立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
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是重新出现的。大家清楚线程的始建和销毁是一个支出相比大的操作,Task.Run()每一次执行将不会立刻创造3个新线程,而是到CLEscort线程池查看是还是不是有闲暇的线程,有的话就取三个线程处理那么些请求,处理完请求后再把线程放回线程池,那么些线程也不会即刻撤除,而是设置为空闲状态,可供线程池再度调度,从而收缩费用。

贰 、异步进步响应能力

  异步对也许引起短路的位移(例如应用程序访问
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>的实例对象有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)方法签名包涵二个 async 修饰符。

  (2)根据约定,异步方法的名目以“Async”后缀为尾声。

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

    ① 假使您的艺术有操作数为
TResult 类型的回来语句,则为 Task<TResult>。

    ② 借使您的法子没有回来语句或具有没有操作数的归来语句,则为 Task。

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

  (4)方法一般包括至少多少个await
表达式,该表明式标记1个点,在该点上,直到等待的异步操作实现章程才能接二连三。 同时,将艺术挂起,并且控制权将回来到点子的调用方。

  在异步方法中,可应用提供的最首要字和品种来提醒供给形成的操作,且编写翻译器会形成其余操作。 

 

④ 、异步方法的控制流(宗旨)

  异步编制程序中最要紧却不易懂的是控制流,即差异方法间的切换。未来,请用一颗感恩的心来察看下图。

www.30064.com 3

  步骤解析:

  ① 事件处理程序调用并听候 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> 重临至调用方。 该任务表示对产生下载字符串长度的整数结果的2个承诺。

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

  在调用方内部(假使那是一个事件处理程序),处理格局将继续。在等候结果前,调用方能够实行不依靠于 AccessTheWebAsync 结果的别的干活,不然就需等候片刻。事件处理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。

  ⑦ GetStringAsync 完成并生成3个字符串结果。 字符串结果不是经过你预期的点子调用 GetStringAsync 所再次回到的。(请牢记,此情势已在步骤 3
中回到3个职务。)相反,字符串结果存款和储蓄在象征实现措施 getStringTask 的天职业中学。 await
运算符从 getStringTask 中寻找结果。赋值语句将追寻到的结果赋给 urlContents。

  ⑧ 当 AccessTheWebAsync 具有字符串结果时,该方法能够测算字符串长度。然后,AccessTheWebAsync 工作也将做到,并且等待事件处理程序可接二连三利用。 

 

  你能够品尝思考一下同步行为和异步行为之间的反差。当其行事做到时(第陆 步)会回去两个联袂方法,但当其行事挂起时(第 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重回3个TResult的值,如上述德姆o中的异步方法重返八个int。

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

一般脾气状下,async/await必须成对出现才有意义,假诺八个方式表明为async,但却未曾行使await关键字,则那一个艺术在举行的时候就被视作同步方法,那时编写翻译器也会抛出警示提醒async修饰的章程中绝非使用await,将被当作一道方法运用。精晓了根本字async\await的风味后,大家来看一下上述德姆o在控制台会输入什么吧。

www.30064.com 4

输出的结果已经很备受关注地报告我们全体实施流程了。GetLengthAsync异步方法刚初始是共同执行的,所以”GetLengthAsync
Start”字符串会被打字与印刷出来,直到遇到第②个await关键字,真正的异步义务GetStringAsync伊始施行,await也就是起到2个标记/唤醒点的功效,同时将控制权放回给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关键字的法则其实便是经过应用2个线程实现异步调用吗?答案是还是不是认的。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 达成并生成1个字符串结果。 字符串结果不是因而你预期的法门调用 GetStringAsync 所再次来到的。(请记住,此方法已在步骤 3
中回到三个职责。)相反,字符串结果存款和储蓄在象征实现章程 getStringTask 的职分中。 await
运算符从 getStringTask 中寻找结果。赋值语句将寻找到的结果赋给 urlContents。

  ⑧ 当 AccessTheWebAsync 具有字符串结果时,该措施能够计算字符串长度。然后,AccessTheWebAsync 工作也将成功,并且等待事件处理程序可继承选拔。 

 

  你能够尝试思考一下同步行为和异步行为之间的差异。当其行事到位时(第陆 步)会重返二个联袂方法,但当其行事挂起时(第 3 步和第 6
步),异步方法会重返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;
        }
    }
}

www.30064.com ,五、线程

  异步方法目的在于成为非阻塞操作。异步方法中的
await 表达式在等候的天职正在运作时不会堵塞当前线程。相反,表明式在继承时登记方式的别的部分并将控制权重回到异步方法的调用方。

  async
和 await 关键字不会促成创建别的线程。因为异步方法不会在其本身线程上运维,因而它不须要二十八线程。唯有当方法处于活动状态时,该办法将在眼下一块上下文中运营并利用线程上的光阴。能够利用 Task.Run 将占用大量CPU
的干活移到后台线程,不过后台线程不会协理正在守候结果的进度变为可用状态。

  对于异步编制程序而言,该基于异步的主意优于大致每种用例中的现有措施。具体而言,此措施比 BackgroundWorker 更适用于
IO 绑定的操作,因为此代码更简便易行且无需提防超越争用标准。结合 Task.Run 使用时,异步编制程序比 BackgroundWorker 更适用于
CPU 绑定的操作,因为异步编程将运转代码的和谐细节与 Task.Run 传输至线程池的办事分别开来。

 

六、async 和 await 修饰符

  当你利用 async 修饰符内定该方式为异步方法时:

  • 可以运用 await 来钦点悬挂点。await
    运算符会告诉编写翻译器,异步方法唯有直到等待的异步进程举行到位,才能继续通过该点往下进行。同时,控制权将回到至异步方法的调用方。await
    表明式中异步方法在挂起后,固然该办法还未曾履行到位并脱离,finally 块中的将不会执行。

  • 标记的异步方法本身能够经过调用它的情势开始展览等待。异步方法中数见不鲜包罗2个或三个await 运算符,当然,3个 await
    表明式都不设有也不会造成编译器错误,不过编写翻译器会时有产生警告,该方法在实践的时候依旧会依据同步方法来推行,async
    其实只是3个标识的功能而已,告诉编写翻译器他“应该”是三个异步方法。

 

六、async 和 await

  即使因而接纳 async 修饰符内定某种情势为异步方法,则会现出下边二种情况。

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

    await
    表明式中异步方法的挂起不能够使该格局退出,并且 finally 块不会运转。

  • 标记的异步方法本人能够因此调用它的点子等待。

  异步方法一般包含await 运算符的一个或多少个匹配项,但缺乏 await
表明式不会招致编写翻译器错误。假设异步方法未使用
await
运算符标记悬挂点,则该措施将作为联合方法执行,不管异步修饰符怎么样。编写翻译器将为此类措施发表叁个警告。

 

七 、再次回到类型和参数音信

  在编写异步方法时,大家多方会选择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 重返类型的异步方法,并且一个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地图