提起.Net中的 async/await,相信很多.neter 第一反应都会是异步编程,其本质是语法糖,但继续追查下去,既然是语法糖,那么经过编译之后,真正的代码是什么样的,如何执行的?带着这些疑问,通过网上资料的查询,可以了解到编译之后,是通过实现 IAsyncStateMachine 的一个状态机来实现的,博客园里大神Jeffcky 已经说得很清楚了,传送门: https://www.cnblogs.com/CreateMyself/p/5983208.html

上述知识对我们理解 async/await 非常重要,但不是本文讨论的侧重点,触发笔者写这篇文章的初衷是:

1.只有Task可以被await吗,await之后就一定是异步执行吗?

答案当然不是,google了一圈后发现,当一个类可以被await,必须满足以下条件:

a.它必须包含 GetAwaiter() 方法(实例方法或者扩展方法) // 手动划重点:扩展方法,聪明的你是不是立马有些思想火花
b.GetAwaiter() 返回awatier实例,并且这个实例包含如下条件:

  • 必须实现 INotifyCompletion 或者 ICriticalNotifyCompletion 接口
  • 必须包含 IsCompleted 公共属性
  • 必须包含 GetResult() 方法,返回void或者其他返回值

上述条件中INotifyCompletion 接口信息如下:

    //
    // 摘要:
    //     Represents an operation that schedules continuations when it completes.
    public interface INotifyCompletion
    {
        //
        // 摘要:
        //     Schedules the continuation action that's invoked when the instance completes.
        //
        // 参数:
        //   continuation:
        //     The action to invoke when the operation completes.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     The continuation argument is null (Nothing in Visual Basic).
        void OnCompleted(Action continuation);
    }

重点上述对于参数 continuation 的解释:委托在操作完成之后调用。此处遗留一个问题:在谁的操作完成之后调用,是怎么调用的?

先把上述问题放一边,我们来自己写一个可以被await的类,并且观察前后执行的顺序以及是否存在线程切换:

 public class Program {
        static async Task Main (string[] args) {
            Console.WriteLine ($"Begin awati,thread id is {Thread.CurrentThread.ManagedThreadId}");
            int result = await new CustomAwaitable ();
            Console.WriteLine ($"End await,result is {result},thread id is {Thread.CurrentThread.ManagedThreadId}");
            await Task.Delay (Timeout.Infinite);
        }
    }

    public class CustomAwaitable : INotifyCompletion {
        public void OnCompleted (Action continuation) {
            Console.WriteLine ($"Invoke continuation action on completed,thread id is {Thread.CurrentThread.ManagedThreadId}");
            continuation?.Invoke ();
        }

        public int GetResult () {
            Console.WriteLine ($"Get result,thread id is {Thread.CurrentThread.ManagedThreadId}");
            return 100;
        }

        public bool IsCompleted { get; set; }

        public CustomAwaitable GetAwaiter(){
            return this;
        }
    }

上述代码中,CustomAwaitable 实例满足了可被await的所有条件,并且正常通过编译,运行后发现结果如下:

PS D:\git\awaitable\src> dotnet run
Begin main,thread id is 1
Get awatier,thread id is 1
Begin Invoke continuation action on completed,thread id is 1
Get result,thread id is 1
End main,result is 100,thread id is 1
End Invoke

根据上述日志,可以看出:

  1. 执行前后线程并未发生切换,所以当我们不假思索的回答 await/async 就是异步编程时,至少是一个不太严谨的答案
  2. 最后执行日志 "End Invoke" 表明:continuation action 这个委托,根据上述调用日志顺序可以大致理解为:编译器将await之后的代码封装为这个 action,在实例完成后调用OnCompleted方法执行了await 之后的代码(注:实际情况比较复杂,如果有多行await,会转换为一个状态机,具体参看文章开头给出的连接)。

2.了解了上述知识之后,那么我们常规所说的await Task异步编程又是怎么回事呢?

  1. 先来看Task部分源码(传送门):

上述红框代码显示,Task在GetAwaiter中创建了 TaskAwaiter对象,并将this传递。

  1. 再来看TaskAwaiter源码(传送门):

看到此处,有了前面的知识,我们会对await task有了更加深入的理解:
Task通过增加一个GetAwatier()函数,同时将自身传递给TaskAwaiter类来实现了await语法糖的支持,同时在执行时,调用GetResult()函数的本质是通过 Task.Wait等待异步线程的执行完成,然后通过回调进行后续的操作。

总结

本文主要对 async/await 语法糖进行分析验证,同时通过对Task源码分析,更加深入的理解此语法糖本身的语法,相信通过通过此文,对大家从多个角度去理解异步编程有帮助,我自己也在不停的学习。

本文代码示例地址:https://github.com/xBoo/awaitable

重新认识 async/await 语法糖的更多相关文章

  1. 抓住异步编程async/await语法糖的牛鼻子: SynchronizationContext

    长话短说,本文带大家抓住异步编程async/await语法糖的牛鼻子: SynchronizationContext 引言 C#异步编程语法糖async/await,使开发者很容易就能编写异步代码. ...

  2. Async/await语法糖实现(Generator)

    // generator也是一种迭代器(Iterator) 有next方法,并返回一个对象{value:...,done:...} function run(generatorFunction) { ...

  3. javascript异步编程之generator(生成器函数)与asnyc/await语法糖

    Generator 异步方案 相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题.但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到 ...

  4. Python PEP 492 中文翻译——协程与async/await语法

    原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...

  5. ReactNative踩坑日志——使用async/await语法解决网络请求的异步导致的指令执行顺序错乱问题

    转载请注明原文地址: ReactNative的fetch是天然的异步请求,因此,如果你在一个代码块中使用了fetch,那么在执行的时候程序不会等待网络响应结束才执行下一条代码,而是会直接按顺序执行完整 ...

  6. C#Framework4.0支持异步async/await语法

    由于用户使用的是XP系统,但是程序里异步都是通过async/await代码来实现的,然而async/await需要Framework4.5版本才可以,而XP系统最高只能支持到Framework4.0, ...

  7. ES8 async/await语法

    Async/await的主要益处是可以避免回调地狱(callback hell)问题 Chromium JavaScript引擎 从v5.5开始支持async/await功能,Chromium Jav ...

  8. python3.6以上 asyncio模块的异步编程模型 async await语法

    这是python3.6以上版本的用法,本例是python3.7.2编写使用asyncio模块的异步编程模型,生产这消费者,异步生产,用sleep来代替IO等待使用async和await语法来进行描述a ...

  9. 让webpack打包支持ES7的async/await语法

    npm install --save-dev babel-plugin-transform-runtime npm install --save babel-runtime .babelrc配置 { ...

随机推荐

  1. Win10《芒果TV》更新v3.6.0秋收版:新增追剧磁贴、记忆续播、跳转列表

    热血青春,唱响革命战歌,<秋收起义>正在芒果TV热播,Win10版<芒果TV>更新v3.6.0秋收版,新增追剧磁贴.记忆续播.跳转列表. Win10版<芒果TV>V ...

  2. Advanced Installer,搜索注册表,根据注册表选择安装路径

    原文:Advanced Installer,搜索注册表,根据注册表选择安装路径 又停了一段时间没有更新了,今天上博客,发现有位朋友就打包的时候需要搜索注册表(不同版本注册表路径不一致,需要搜索多次来确 ...

  3. Linux 桌面玩家指南:20. 把 Linux 系统装入 U 盘打包带走

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  4. 很幽默的讲解六种Socket IO模型 Delphi版本(自己Select查看,WM_SOCKET消息通知,WSAEventSelect自动收取,Overlapped I/O 事件通知模型,Overlapped I/O 完成例程模型,IOCP模型机器人)

    很幽默的讲解六种Socket IO模型(转)本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教. 一:select模型 二:WSAAsyncSel ...

  5. c# 关于TreeView的一点性能问题

    我们要知道,treeview在新增或删除treeNode的时候会进行重绘,这也就是为什么大量数据的时候,treeview很卡.很慢的原因, 那么我们这样 treeview1.BeginUpdate() ...

  6. STL-空间配置器、迭代器、traits编程技巧

    目录 内存分配和释放 对象的构造和析构 traits要解决的问题 内嵌类别声明解决非指针迭代器的情况 使用模板特例化解决普通指针的情况 迭代器相应类别 内存分配和释放 STL中有两个分配器,一级分配器 ...

  7. spring boot 2.x 系列——spring-boot 集成 Swagger2 打造在线接口文档

    文章目录 一.Springfox 与 Swagger 简介 1.1 Springfox 1.2 Swagger 1.3 OpenApi.Swagger.Springfox的关系 二.spring bo ...

  8. 【Zookeeper02】ZK的作用以及使用

    上一篇介绍了ZK的安装以及集群的搭建,这只能算是个软件安装过程,具体是做什么的.怎么用也没有做解释,这一篇中博主就自己的私人理解简单写一下: 1.是什么: a.Zookeeper是一个分布式协调服务, ...

  9. SSM(一)Mybatis基础

    1.持久化与ORM 持久化是数据在内存与硬盘间相互转化的过程 ORM即对象关系映射 程序员使用面向对象的思维方式处理数据,每个对象都是一个pojo.但是保存数据的时候,却以关系型数据库的方式存储.所以 ...

  10. 使用elasticsearch启动项目报错failed to load elasticsearch nodes 。。。。。No type specified for field [name]

    failed to load elasticsearch nodes .....No type specified for field [name]翻译: 加载ElasticSearch节点失败... ...