c#中的async和await 以及编译原理

Async/Await

c#中的TAP提供了一套较高级的异步编程,将异步代码可以编写的向同步代码一样优美。其中主要用到了async和await,下面是一段简单的示例代码

public async Task Launch()
{
    var network = await Network.Connect();
    if(!network.IsSucceed)
    {
        ShowAlert();
        return;
    }
    
    var user = await Network.Login();
    if(user.IsSucceed)
    {
        //do something.
    }
}

Async

async是c#中的一个关键字,用于修饰函数、lambda表达式或者匿名函数,使其变成异步方法。当async修改的方法执行过程大致如下:

NO
YES
NO
YES
complete
Start
同步执行到第一次遇到await处
exist await?
over
isComplete?
block and register callback

Await

await是c#中的操作符,await用于判断awaitable的错误是否完成,如果完成离开执行后续操作;如果没有完成则会block并让出当前线程,
等待异步操作完成之后再继续后续工作。

代码分析

下面是一段简短的示例。

public async Task TestTask()
{
    Console.WriteLine("start");
    await Task.Delay(TimeSpan.FromSeconds(1)); //task1
    Console.WriteLine("end");
    await Task.Delay(TimeSpan.FromSeconds(1)); //task2
    Console.WriteLine("end1");
}

中间部分代码如下,如果有兴趣可以自己通过编译器查看完整IL。

IL_0000: ldarg.0      // this
IL_0001: ldfld        int32 TaskExample.Test/'<TestTask>d__1'::'<>1__state'
IL_0006: stloc.0      // V_0

IL_0007: ldloc.0      // V_0
IL_0008: brfalse.s    IL_0012
IL_000a: br.s         IL_000c
IL_000c: ldloc.0      // V_0
IL_000d: ldc.i4.1
IL_000e: beq.s        IL_0014
IL_0010: br.s         IL_0019
IL_0012: br.s         IL_006e
IL_0014: br           IL_00e3

// [16 9 - 16 10]
IL_0019: nop

// [17 13 - 17 40]
IL_001a: ldstr        "start"
IL_001f: call         void [System.Console]System.Console::WriteLine(string)
IL_0024: nop

IL_003e: ldloca.s     V_1
IL_0040: call         instance bool [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted()
IL_0045: brtrue.s     IL_008a
IL_0047: ldarg.0      // this
IL_0048: ldc.i4.0
IL_0049: dup
IL_004a: stloc.0      // V_0
IL_004b: stfld        int32 TaskExample.Test/'<TestTask>d__1'::'<>1__state'
IL_0050: ldarg.0      // this
IL_0051: ldloc.1      // V_1
IL_0052: stfld        valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter TaskExample.Test/'<TestTask>d__1'::'<>u__1'
IL_0057: ldarg.0      // this
IL_0058: stloc.2      // V_2
IL_0059: ldarg.0      // this
IL_005a: ldflda       valuetype [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder TaskExample.Test/'<TestTask>d__1'::'<>t__builder'
IL_005f: ldloca.s     V_1
IL_0061: ldloca.s     V_2
IL_0063: call         instance void [System.Threading.Tasks]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter, class TaskExample.Test/'<TestTask>d__1'>(!!0/*valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter*/&, !!1/*class TaskExample.Test/'<TestTask>d__1'*/&)
IL_0068: nop
IL_0069: leave        IL_0142
IL_006e: ldarg.0      // this
IL_006f: ldfld        valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter TaskExample.Test/'<TestTask>d__1'::'<>u__1'
IL_0074: stloc.1      // V_1
IL_0075: ldarg.0      // this
IL_0076: ldflda       valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter TaskExample.Test/'<TestTask>d__1'::'<>u__1'
IL_007b: initobj      [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter
IL_0081: ldarg.0      // this
IL_0082: ldc.i4.m1
IL_0083: dup
IL_0084: stloc.0      // V_0
IL_0085: stfld        int32 TaskExample.Test/'<TestTask>d__1'::'<>1__state'
IL_008a: ldloca.s     V_1
IL_008c: call         instance void [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::GetResult()
IL_0091: nop

我们先分析第一段

IL_0007: ldloc.0 // V_0
IL_0008: brfalse.s IL_0012
IL_000a: br.s IL_000c
IL_000c: ldloc.0 // V_0
IL_000d: ldc.i4.1
IL_000e: beq.s IL_0014
IL_0010: br.s IL_0019
IL_0012: br.s IL_006e
IL_0014: br IL_00e3

上面这一段主要是来判断<>1__state这个状态的值,通过不同的值跳转到不同的代码,转化为伪代码如下:

switch(1__state)
{
    case 0:
        //读取第一个task的返回值,然后执行打印"end",然后执行task1。
        break;
    case 1:
        //读取第二个task的返回值,然后执行打印"end1",然后结束
        breadk;
    default:
        //打印"start",然后执行第一个task
        break;
}

IL_0040: call instance bool [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted()
IL_0045: brtrue.s IL_008a

这一段主要是判断task是否完成,如果完成直接跳转到getResult那里,否则调用状态机的AwaitUnsafeOnCompleted方法。
上面还涉及到AsyncTaskMethodBuilder、GetAwaiter以及get_IsCompleted等方法的调用,这些都由编译器自动为我们完成,在大多数情况下我们不需要
考虑这些内容,除非我们想定义个性化的task。

自定义awaitable方法

通过上面的简单介绍我们可以看出,async/await往往会和task一起使用,并不是所有的方法都可以被await。那如何才能被await呢?需要T满足下面几个条件:

  1. T是编译时的动态类型
  2. T有一个无参的GetAwaiter方法,并返回类型为A
  • A必须实现接口INotifyCompletion
  • A要有一个bool的IsCompleted属性
  • A要有一个GetResult方法.

更多的文档可以参见
Awaitable expressions

未完待续…

猜你喜欢

转载自blog.csdn.net/scdnshijiemengxiang/article/details/128741719