浅谈Async和Await如何简化异步编程(几个实例让你彻底明白)

2019-12-30 15:22:57于海丽


// 编译器为按钮Click事件生成的代码
private void btnClick_Click(object sender, EventArgs e)
{
  <btnClick_Click>d__0 d__;
  d__.<>4__this = this;
  d__.sender = sender;
  d__.e = e;
  d__.<>t__builder = AsyncVoidMethodBuilder.Create();
  d__.<>1__state = -1;
  d__.<>t__builder.Start<<btnClick_Click>d__0>(ref d__);
}

看到上面的代码,作为程序员的我想说——编译器你怎么可以这样呢?怎么可以任意篡改我的代码呢?这样不是侵犯我的版权了吗?你要改最起码应该告诉我一声吧,如果我的源码看到它在编译器中的实现是上面那样的,我相信我的源码会说——难道我中了世间上最恶毒的面目全非脚吗? 好吧,为了让大家更好地理清编译器背后到底做了什么事情,下面就顺着上面的代码摸瓜,我也来展示耍一套还我漂漂拳来帮助大家找到编译器代码和源码的对应关系。我的分析思路为:

1、提出问题——我的click事件的源码到哪里去了呢?

从编译器代码我们可以看到,前面的7句代码都是对某个类进行赋值的操作,最真正起作用的就是最后Start方法的调用。这里又产生了几个疑问——d__0是什么类型? 该类型中的<>t__builder字段类型的Start方法到底是做什么用的? 有了这两个疑问,我们就点击d__0(反射工具可以让我们直接点击查看)来看看它是什么类型   


// <btnClick_Click>d__0类型的定义,从下面代码可以看出它是一个结构体
// 该类型是编译器生成的一个嵌入类型
// 看到该类型的实现有没有让你联想到什么?
private struct <btnClick_Click>d__0 : IAsyncStateMachine
{
  // Fields
  public int <>1__state;
  public Form1 <>4__this;
  public AsyncVoidMethodBuilder <>t__builder;
  private object <>t__stack;
  private TaskAwaiter<long> <>u__$awaiter2;
  public long <length>5__1;
  public EventArgs e;
  public object sender;

  // Methods
  private void MoveNext()
  {
    try
    {
      TaskAwaiter<long> CS$0$0001;
      bool <>t__doFinallyBodies = true;
      switch (this.<>1__state)
      {
        case -3:
          goto Label_010E;

        case 0:
          break;

        default:
            // 获取用于等待Task(任务)的等待者。你要知道某个任务是否完成,我们就需要一个等待者对象对该任务进行一个监控,所以微软就定义了一个等待者对象的
            // 从这里可以看出,其实async和await关键字背后的实现原理是基于任务的异步编程模式(TAP)
          // 这里代码是在线程池线程上运行的
          CS$0$0001 = this.<>4__this.AccessWebAsync().GetAwaiter();
            // 如果任务完成就调转到Label_007A部分的代码
          if (CS$0$0001.IsCompleted)
          {
            goto Label_007A;
          }
           
          // 设置状态为0为了退出回调方法。
          this.<>1__state = 0;
          this.<>u__$awaiter2 = CS$0$0001;
            // 这个代码是做什么用的呢?让我们带着问题看下面的分析
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<long>, Form1.<btnClick_Click>d__0>(ref CS$0$0001, ref this);
          <>t__doFinallyBodies = false;
            // 返回到调用线程,即GUI线程,这也是该方法不会堵塞GUI线程的原因,不管任务是否完成都返回到GUI线程
          return;
      }
      // 当任务完成时,不会执行下面的代码,会直接执行Label_007A中代码
      CS$0$0001 = this.<>u__$awaiter2;
      this.<>u__$awaiter2 = new TaskAwaiter<long>();
      // 为了使再次回调MoveNext代码
      this.<>1__state = -1;
    Label_007A:
      // 下面代码是在GUI线程上执行的
      CS$0$0001 = new TaskAwaiter<long>();
      long CS$0$0003 = CS$0$0001.GetResult();
      this.<length>5__1 = CS$0$0003;
        // 我们源码中的代码这里的
      this.<>4__this.OtherWork();
      this.<>4__this.richTextBox1.Text = this.<>4__this.richTextBox1.Text + string.Format("n 回复的字节长度为: {0}.rn", this.<length>5__1);
      this.<>4__this.txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString();
    }
    catch (Exception <>t__ex)
    {
      this.<>1__state = -2;
      this.<>t__builder.SetException(<>t__ex);
      return;
    }
  Label_010E:
    this.<>1__state = -2;
    this.<>t__builder.SetResult();
  }

  [DebuggerHidden]
  private void SetStateMachine(IAsyncStateMachine param0)
  {
    this.<>t__builder.SetStateMachine(param0);
  }
}