HTML5技术

C#与C++的发展历程第三 - C#5.0异步编程巅峰 - hystar(2)

字号+ 作者:H5之家 来源:博客园 2016-01-15 10:03 我要评论( )

Task同时服务于并发编程和异步编程(在Jeff rey Richter 的CLR via C#中分别称这两种模式为计算限制的异步操作和IO限制的异步操作 ,仔细想想这称呼也很贴切),这里主要讨论下Task和异步编程的相关的机制。其中最

Task同时服务于并发编程和异步编程(在Jeffrey Richter的CLR via C#中分别称这两种模式为计算限制的异步操作和IO限制的异步操作,仔细想想这称呼也很贴切),这里主要讨论下Task和异步编程的相关的机制。其中最关键的一点就是Task是一个awaitable对象,这是其可以用于异步编程的基础。除了Task,还有很多类型也是awaitable的,如ConfigureAwait方法返回的ConfiguredTaskAwaitable、WinRT平台中的IAsyncInfo(这个后文有详细说明)等。要成为一个awaitable类型需要符合哪些条件呢?其实就一点,其中有一个GetAwaiter()方法,该方法返回一个awaiter。那什么是awaiter对象呢?满足如下3点条件即可:

  • 实现INotifyCompletion或ICriticalNotifyCompletion接口

  • 有bool类型的IsCompleted属性

  • 有一个GetResult()来返回结果,或是返回void

  • awaitable和awaiter的关系正如IEnumerable和IEnumerator的关系一样。推而广之,下面要介绍的async/await的幕后实现方式和处理yield语法糖的实现方式差不多。

    Task类型的GetAwaiter()返回的awaiter是TaskAwaiter类型。这个TaskAwaiter很简单基本上就是刚刚满足上面介绍的awaiter的基本要求。类似于EAP,当异步操作执行完毕后,将通过OnCompleted参数设置的回调继续向下执行,并可以由GetResult获取执行结果。

     

    简要了解过Task,再来看一下本节的重点 - async异步方法。async/await模式的异步也出来很久了,相关文章一大片,这里介绍下重点介绍下一些不容易理解和值得重点关注的点。我相信我曾经碰到的困惑也是很多人的遇到的困惑,写出来和大家共同探讨。

    语法糖

    对async/await有了解的朋友都知道这两个关键字最终会被编译为.NET中和异步相关的状态机的代码。这一部分来具体看一下这些代码,了解它们后我们可以更准确的去使用async/await同时也能理解这种模式下异常和取消是怎样完成的。

    先来展示下用于分析反编译代码的例子,一个控制台项目的代码,这是能想到的展示异步方法最简单的例子了,而且和实际项目中常用的代码结构也差不太多:

    //实体类 public class User {     public int Id { get; set; }     public string UserName { get; set; } = "hystar";     public string Email { get; set; } } class Program {     static void Main(string[] args)     {         var service = new Service(new Repository());         var name = service.GetUserName(1).Result;         Console.WriteLine(name);     } } public class Service {     private readonly Repository _repository;     public Service(Repository repository)     {         _repository = repository;     }     public async Task<string> GetUserName(int id)     {         var name = await _repository.GetById(id);         return name;     } } public class Repository {     private DbContext _dbContext;     private DbSet<User> _set;     public Repository()     {         _dbContext = new DbContext("");         _set = _dbContext.Set<User>();     }     public async Task<string> GetById(int id)     {          //IO...         var user = await _set.FindAsync(id);         return user.UserName;     } }

    注意:控制台版本的示例代码中在Main函数中使用了task.Result来获取异步结果,需要注意这是一种阻塞模式,在除控制台之外的UI环境不要使用类似Result属性这样会阻塞的方法,它们会导致UI线程死锁。而对于没有SynchronizationContext的控制台应用确是再合适不过了。对于没有返回值的Task,可以使用Wait()方法等待其完成。

    这里使用ILSpy去查看反编译后的代码,而且注意要将ILSpy选项中的Decompile async methods (async/await)禁用(如下图),否则ILSpy会很智能将IL反编译为有async/await关键字的C#代码。另外我也尝试过Telerik JustDecompile等工具,但是能完整展示反编译出的状态机的只有ILSpy。

    图2

    另外注意,应该选择Release版本的代码去查看,这是在一个Stackoverflow回答中看到的,说是有啥不同,具体也没仔细看,这里知道选择Release版exe/dll反编译就好了。下面以Service类为例来看一下反编译后的代码:

    图3

    通过图上的注释可以看到代码主要由两大部分构成,Service类原有的代码和一个由编译器生成的状态机,下面分别具体了解下它们都做了什么。依然是以图片加注释为主,重要的部分会在图后给出文字说明。

    图4

    通过上图中的注释可以大致了解GetUserName方法编译后的样子。我们详细介绍下其中几个点,首先是AsyncTaskMethodBuilder<T>,我感觉很有必要列出其代码一看:

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 2年前端学习历程,与找不到工作的悲愤与吐槽!(100%真实经历,看博主怎么一步步走向失业) - 蒋启钲

      2年前端学习历程,与找不到工作的悲愤与吐槽!(100%真实经历,看博

      2017-03-29 11:00

    • 开源第三方登录组件OAuthLogin2.0 支持QQ,阿里巴巴,淘宝,京东,蘑菇街,有赞等平台 - 大壮他哥

      开源第三方登录组件OAuthLogin2.0 支持QQ,阿里巴巴,淘宝,京东,蘑菇街

      2017-01-20 15:00

    • 从零到百亿互联网金融架构发展史 - 纯洁的微笑

      从零到百亿互联网金融架构发展史 - 纯洁的微笑

      2017-01-14 13:00

    • 记一次企业级爬虫系统升级改造(四):爬取微信公众号文章(通过搜狗与新榜等第三方平台) - 彩色铅笔

      记一次企业级爬虫系统升级改造(四):爬取微信公众号文章(通过搜狗

      2017-01-12 10:01

    网友点评
    ;