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修改的方法执行过程大致如下:
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满足下面几个条件:
- T是编译时的动态类型
- T有一个无参的GetAwaiter方法,并返回类型为A
- A必须实现接口INotifyCompletion
- A要有一个bool的IsCompleted属性
- A要有一个GetResult方法.
更多的文档可以参见
Awaitable expressions
未完待续…