zoukankan      html  css  js  c++  java
  • async-await原理解析

        在用async包裹的方法体中,可以使用await关键字以同步的方式编写异步调用的代码。那么它的内部实现原理是什么样的呢?我们是否可以自定义await以实现定制性的需求呢?先来看一个简单的例子:

     1     class Test {
     2         public static void Main (string[] args) {
     3             Task.Run (new Func<Task<string>>(task1));
     4             Console.ReadLine ();
     5         }
     6 
     7         private async static Task<string> task1() {
     8             string ret = await task2 ();
     9             Console.WriteLine ("Await Task Result:" + ret);
    10             return ret;
    11         }
    12 
    13         private static Task<string> task2() {
    14             return Task.FromResult<string> ("Task2");
    15         }
    16     }

        通过ILSpy反编译(要关闭"视图-选项-反编译await/async"菜单项),得到如下代码:

     1     internal class Test
     2     {
     3         [CompilerGenerated]
     4         [StructLayout(LayoutKind.Auto)]
     5         private struct <task1>d__0 : IAsyncStateMachine
     6         {
     7             public int <>1__state;
     9             public AsyncTaskMethodBuilder<string> <>t__builder; 
    11             public string <ret>5__1;
    13             private TaskAwaiter<string> <>u__$awaiter2;
    15             private object <>t__stack;
    16 
    17             void IAsyncStateMachine.MoveNext()
    18             {
    19                 string result;
    20                 try
    21                 {
    22                     int num = this.<>1__state;
    23                     if (num != -3)
    24                     {
    25                         TaskAwaiter<string> taskAwaiter;
    26                         if (num != 0)
    27                         {
    28                             taskAwaiter = Test.task2().GetAwaiter();
    29                             if (!taskAwaiter.IsCompleted)
    30                             {
    31                                 this.<>1__state = 0;
    32                                 this.<>u__$awaiter2 = taskAwaiter;
    33                                 this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Test.<task1>d__0>(ref taskAwaiter, ref this);
    34                                 return;
    35                             }
    36                         }
    37                         else
    38                         {
    39                             taskAwaiter = this.<>u__$awaiter2;
    40                             this.<>u__$awaiter2 = default(TaskAwaiter<string>);
    41                             this.<>1__state = -1;
    42                         }
    43                         string arg_86_0 = taskAwaiter.GetResult();
    44                         taskAwaiter = default(TaskAwaiter<string>);
    45                         string text = arg_86_0;
    46                         this.<ret>5__1 = text;
    47                         Console.WriteLine("Await Task Result:" + this.<ret>5__1);
    48                         result = this.<ret>5__1;
    49                     }
    50                 }
    51                 catch (Exception exception)
    52                 {
    53                     this.<>1__state = -2;
    54                     this.<>t__builder.SetException(exception);
    55                     return;
    56                 }
    57                 this.<>1__state = -2;
    58                 this.<>t__builder.SetResult(result);
    59             }
    60 
    61             [DebuggerHidden]
    62             void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
    63             {
    64                 this.<>t__builder.SetStateMachine(param0);
    65             }
    66         }
    67 
    68         public static void Main(string[] args)
    69         {
    70             Task.Run<string>(new Func<Task<string>>(Test.task1));
    71             Console.ReadLine();
    72         }
    73 
    74         [DebuggerStepThrough, AsyncStateMachine(typeof(Test.<task1>d__0))]
    75         private static Task<string> task1()
    76         {
    77             Test.<task1>d__0 <task1>d__;
    78             <task1>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
    79             <task1>d__.<>1__state = -1;
    80             AsyncTaskMethodBuilder<string> <>t__builder = <task1>d__.<>t__builder;
    81             <>t__builder.Start<Test.<task1>d__0>(ref <task1>d__);
    82             return <task1>d__.<>t__builder.Task;
    83         }
    84 
    85         private static Task<string> task2()
    86         {
    87             return Task.FromResult<string>("Task2");
    88         }
    89     }

         按照代码的调用顺序,我们关注下task1()的内部实现。

        首先是初始化结构体<task1>d_0的实例<task1>d__。那么<task1>d__0是个什么东东呢?由编译器的生成代码中可以看到,它是一个实现了IAsyncStateMachine接口的结构体,而用户代码则被编译器重新组织进了MoveNext()方法中。<task1>d__0有个内部状态成员<>1__state,MoveNext()方法根据这个状态调转到相应的代码块中加以执行。

        了解了<task1>d__0的声明实现,再看下task1()方法中的具体调用。在创建实例<task1>d__之后,设置初始状态<>1__state为-1,并调用<>t__builder的Start方法。不难推断,在Start方法中会调用<task1>d__.MoveNext(),此时内部状态为-1,会先调用Test.task2().GetAwaiter()获取其所关联的TaskAwaiter实例。如果awaiter当前是未结束的话,则设置<>1__state为0,并将当前<task1>d__作为参数关联到TaskAwaiter实例的onCompletedContinuation回调延续中去。当未来某个时刻,TaskAwaiter所关联的Task任务结束时,会设置awaiter的异步结果并触发回调延续,导致调用<task1>d__.MoveNext()方法,并最终跳转到用户代码块中,获取awaiter的异步结果并交由用户代码处理。这个回调,基于Task.ConfigureAwait(true/false)的不同,会在后续切换到当前线程或是从线程池中取了一个空闲线程来处理(更细节可参考.net源码分析)。

        这里要顺便提一句,在本例中,通过Task.Run创建了taskX1,await之后的代码与taskX1没有任何关系,从编译器生成的代码来看,在调用task1()方法并调用<task1>d__.Start()方法之后taskX便结束了,虽然task1()方法返回了新的Task<string>实例,但是只是特定类型的返回值而已,与taskX1或Task没有任何关系。

        由以上分析可以看到,async/await只是一个语法糖,async告知编译器要生成状态机代码,await则是配合生成GetAwaiter(),并封装跳转的用户代码块。除此之外,async/await与Task没有任何直接关系。而TaskAwaiter的作用,是实现INotifyCompletion(在System.Runtime.CompilerServices命名空间)以桥接异步回调过程。那么第二个自定义await的问题便一目了然了:任何类型,只需要实现GetAwaiter()方法以返回INotifyCompletion实例,便可以被await。

        举个例子:

     1     class TestAwaiter<T> : INotifyCompletion {
     2         private T result;
     3         private Action continuation;
     4 
     5         // INotifyCompletion Implement
     6         public void OnCompleted(Action continuation) { this.continuation = continuation; }
     7 
     8         // Compiler Call Methods
     9         public bool IsCompleted { get; private set; }
    10         public T GetResult() { return result; }
    11         public TestAwaiter<T> GetAwaiter() { return this; }

    // Self Call Methods
    12 public void SetResult(T ret) { 13 result = ret; 14 if (continuation != null) { 15 continuation (); 16 } 17 } 18 } 19 20 class Test { 21 public static void Main (string[] args) { 22 Task.Run (new Action(task1)); 23 Console.ReadLine (); 24 } 25 26 private async static void task1() { 27 Console.WriteLine ("Begin await:"); 28 int ret = await testAwaiter (); 29 Console.WriteLine ("Await Task Result:" + ret); 30 } 31 32 private static TestAwaiter<int> testAwaiter() { 33 TestAwaiter<int> awaiter = new TestAwaiter<int> (); 34 ThreadPool.QueueUserWorkItem (_ => { 35 Thread.Sleep(3000); 36 awaiter.SetResult (100); 37 }); 38 return awaiter; 39 } 40 }

        这里没有再定义单独的类型以返回TestAwaiter,而是把二者都封装在了TestAwaiter内部。运行结果如下:

        Begin await:

        Await Task Result:100

  • 相关阅读:
    POJ 3093 Margaritas on the River Walk(背包)
    BZOJ 2287 【POJ Challenge】消失之物(DP+容斥)
    WC2017 Day1
    WC2017 Day0
    WC2017 Conclusion
    WC2017 Day6
    UOJ #58 糖果公园
    WC2017 Day5
    codevs 1946 阿狸的打字机
    HDU 2457 DNA_repair
  • 原文地址:https://www.cnblogs.com/Jackie-Snow/p/6542372.html
Copyright © 2011-2022 走看看