从新认识 async/await 语法糖

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

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

1.只有Task能够被await吗,await以后就必定是异步执行吗?

答案固然不是,google了一圈后发现,当一个类能够被await,必须知足如下条件:github

a. 它必须包含 GetAwaiter() 方法(实例方法或者扩展方法) // 手动划重点:扩展方法,聪明的你是否是立马有些思想火花编程

b. GetAwaiter() 返回awatier实例,而且这个实例包含以下条件:异步

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

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

//
    // 摘要:
    //     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

相关文章
相关标签/搜索