栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

.Net填坑,C#中的async,await以及编译器给我们做了什么,后面细化再排版

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

.Net填坑,C#中的async,await以及编译器给我们做了什么,后面细化再排版

要点:1.函数增加async之后,就会编译器就会帮我我们生成一个状态机代码

static async  void BBB()
        {
            Console.WriteLine("123");
            
        }

编译器帮助生成的

[AsyncStateMachine(typeof (Program.u003CBBBu003Ed__5))]
    [DebuggerStepThrough]
    private static void BBB()
    {
      Program.u003CBBBu003Ed__5 stateMachine = new Program.u003CBBBu003Ed__5();
      stateMachine.u003Cu003Et__builder = AsyncVoidMethodBuilder.Create();
      stateMachine.u003Cu003E1__state = -1;
      stateMachine.u003Cu003Et__builder.Start(ref stateMachine);
    }

AsyncVoidMethodBuilder类型根据async函数返回值生成

private sealed class u003CBBBu003Ed__5 : IAsyncStateMachine
    {
      public int u003Cu003E1__state;
      public AsyncVoidMethodBuilder u003Cu003Et__builder;

      public u003CBBBu003Ed__5()
      {
        base.u002Ector();
      }

      void IAsyncStateMachine.MoveNext()
      {
        int num = this.u003Cu003E1__state;
        try
        {
          Console.WriteLine("123");
        }
        catch (Exception ex)
        {
          this.u003Cu003E1__state = -2;
          this.u003Cu003Et__builder.SetException(ex);
          return;
        }
        this.u003Cu003E1__state = -2;
        this.u003Cu003Et__builder.SetResult();
      }

      [DebuggerHidden]
      void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
      {
      }
    }

直接调用状态机的movenext,程序就直接往下走了。

如果async函数中,带有await,则会根据await 得到的awaiter类型,生成对应的代码

static async void BBB()
    {
        await Task.Delay(1000);
        Console.WriteLine("Hello, World!");
    }
[AsyncStateMachine(typeof (Program.u003CBBBu003Ed__1))]
  [DebuggerStepThrough]
  private static void BBB()
  {
    Program.u003CBBBu003Ed__1 stateMachine = new Program.u003CBBBu003Ed__1();
    stateMachine.u003Cu003Et__builder = AsyncVoidMethodBuilder.Create();
    stateMachine.u003Cu003E1__state = -1;
    stateMachine.u003Cu003Et__builder.Start(ref stateMachine);
  }
[CompilerGenerated]
  private sealed class u003CBBBu003Ed__1 : IAsyncStateMachine
  {
    public int u003Cu003E1__state;
    public AsyncVoidMethodBuilder u003Cu003Et__builder;
    private TaskAwaiter u003Cu003Eu__1;

    public u003CBBBu003Ed__1()
    {
      base.u002Ector();
    }

    void IAsyncStateMachine.MoveNext()
    {
      int num1 = this.u003Cu003E1__state;
      try
      {
        TaskAwaiter awaiter;
        int num2;
        if (num1 != 0)
        {
          awaiter = Task.Delay(1000).GetAwaiter();
          if (!awaiter.IsCompleted)
          {
            this.u003Cu003E1__state = num2 = 0;
            this.u003Cu003Eu__1 = awaiter;
            Program.u003CBBBu003Ed__1 stateMachine = this;
            this.u003Cu003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
            return;
          }
        }
        else
        {
          awaiter = this.u003Cu003Eu__1;
          this.u003Cu003Eu__1 = new TaskAwaiter();
          this.u003Cu003E1__state = num2 = -1;
        }
        awaiter.GetResult();
        Console.WriteLine("Hello, World!");
      }
      catch (Exception ex)
      {
        this.u003Cu003E1__state = -2;
        this.u003Cu003Et__builder.SetException(ex);
        return;
      }
      this.u003Cu003E1__state = -2;
      this.u003Cu003Et__builder.SetResult();
    }

    [DebuggerHidden]
    void IAsyncStateMachine.SetStateMachine([Nullable(1)] IAsyncStateMachine stateMachine)
    {
    }
  }

因此像上面的代码,BBB函数中,虽然用了async标记,但是在调用BBB函数的地方,是不能用await的,因为BBB是void,外层函数没东西可以等待,改为下面

static async Task BBB()
    {
        await Task.Delay(1000);
        Console.WriteLine("Hello, World!");
    }

这样外层调用函数可以await BBB()了,其实BBB函数已经两个Task类实例(一个是Task.Delay,一个是AsyncTaskMethodBuilder类提供的,BBB函数中的状态机代码没变)

private static Task BBB()
  {
    Program.u003CBBBu003Ed__1 stateMachine = new Program.u003CBBBu003Ed__1();
    stateMachine.u003Cu003Et__builder = AsyncTaskMethodBuilder.Create();
    stateMachine.u003Cu003E1__state = -1;
    stateMachine.u003Cu003Et__builder.Start(ref stateMachine);
    return stateMachine.u003Cu003Et__builder.Task;
  }

这样当外层函数调用await BBB()时,返回的AsyncTaskMethodBuilder.Task中的awaiter没有完成,只有BBB函数中状态机完成,才会调用

this.u003Cu003E1__state = -2;
      this.u003Cu003Et__builder.SetResult();

然后外层的await中状态机才能获取到BBB函数返回的Task完成的状态,继续执行下去。然后一层套一层继续下去,可以一直等待。

那么如果是带返回值的呢,如下:

static async Task BBB()
    {
        await Task.Delay(1000);
        Console.WriteLine("Hello, World!");
        return "123";
    }

生成核心的movenext代码如下:

void IAsyncStateMachine.MoveNext()
    {
      int num1 = this.u003Cu003E1__state;
      string result;
      try
      {
        TaskAwaiter awaiter;
        int num2;
        if (num1 != 0)
        {
          awaiter = Task.Delay(1000).GetAwaiter();
          if (!awaiter.IsCompleted)
          {
            this.u003Cu003E1__state = num2 = 0;
            this.u003Cu003Eu__1 = awaiter;
            Program.u003CBBBu003Ed__1 stateMachine = this;
            this.u003Cu003Et__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
            return;
          }
        }
        else
        {
          awaiter = this.u003Cu003Eu__1;
          this.u003Cu003Eu__1 = new TaskAwaiter();
          this.u003Cu003E1__state = num2 = -1;
        }
        awaiter.GetResult();
        Console.WriteLine("Hello, World!");
        result = "123";
      }
      catch (Exception ex)
      {
        this.u003Cu003E1__state = -2;
        this.u003Cu003Et__builder.SetException(ex);
        return;
      }
      this.u003Cu003E1__state = -2;
      this.u003Cu003Et__builder.SetResult(result);
    }

最重要的就是:this.u003Cu003Et__builder.SetResult(result);
就是说状态机走完了,将后续函数要返回的值,先声明一个string result;,状态走完,设置这个result值,然后传给builder的SetResult方法,内部会调用对应Task的方法来设置结果,这样外层函数await的时候,也能拿到这个结果。

总结:函数标记async后,就会生成状态机代码,而且状态机根据本函数返回值,生成对应的builder,内部可能包含一个可以被等待的对象(Task.GetAwaiter(),返回的对象实现了ICriticalNotifyCompletion接口就算可等待对象),这样外层才能调用await。
async函数内部,使用了await 来等待一个可等待对象时(比如Task),会在状态机中生成,对应的代码,并将awaiter与状态机的movenext函数关联起来,当有任务完成时,会调用awaier的代码来调用movenext代码,再次回到状态机中进行下一步。

ET框架中,实现了自己的ETTask(他本身实现了接口ICriticalNotifyCompletion,所以ETTask.GetAwaiter()返回自身就可以了,且通过接口实现了,状态机moveNext方法与ETTask关联)。同时也实现了ETTask自己的类Builder类ETAsyncTaskMethodBuilder。
内部封装了一个ETTvoid,用途就是使得在ETTASK同步调用中,等待一下自己,然后等待自己的async函数返回ETVoid,对应有AsyncETVoidMethodBuilder,ETTASK中调用Coroutine,会等待自身完成,但是没有后续代码,外层调用ETTASK的地方就不用await了,这样异常也不会丢失。

写的很概括,后面有空再排版与优化这块,先挖坑

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/908676.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号