C#中await执行顺序不符预期?两种异步调用耗时差异咨询
C# 异步await调用耗时差异底层原理
很多刚接触C#异步的开发者会有一个认知误区:以为await操作才会触发异步任务启动。实际上C#中所有async方法返回的都是已启动的热任务(Hot Task):调用async方法的瞬间,方法就会同步执行到内部第一个await的未完成异步操作处,才会返回Task实例给调用方,后续逻辑在异步操作完成后通过回调执行,await的作用仅仅是等待任务完成、获取返回结果,本身不会触发任务启动。
逐行await写法总耗时6秒的原因
对应代码如下:
int a = await Task1(); int b = await Task2(); int c = await Task3(); return a+b+c;
这段代码是纯串行执行逻辑,流程如下:
- 执行到第一行时才会调用
Task1()启动任务,碰到内部的Task.Delay(1000)后,当前执行流就在此处挂起等待,整整1秒后Task1执行完毕返回10,才会进入下一行代码。 - Task1完全结束后才会调用
Task2()启动第二个任务,再等待2秒拿到返回值10,此时距离程序启动已经过去了1+2=3秒。 - Task2完全结束后才会调用
Task3()启动第三个任务,再等待3秒拿到返回值10,总耗时就是三个任务的等待时长累加:1+2+3=6秒。
这种写法下三个任务完全没有并发,和同步顺序调用的耗时没有区别。
先获取Task实例再await总耗时3秒的原因
对应代码如下:
var a = Task1(); var b = Task2(); var c = Task3(); return await a + await b + await c;
这段代码是并发执行逻辑,流程如下:
- 第一行调用
Task1()的瞬间,第一个任务就启动了,碰到内部的Task.Delay(1000)后立刻返回Task实例赋值给a,执行流不会等待,直接进入下一行。 - 第二行调用
Task2()的瞬间,第二个任务同步启动,碰到内部的Task.Delay(2000)后返回Task实例赋值给b,执行流继续往下走。 - 第三行调用
Task3()的瞬间,第三个任务同步启动,碰到内部的Task.Delay(3000)后返回Task实例赋值给c。
这三行代码执行仅耗时几微秒,几乎可以忽略,此时三个延时任务已经处于同时运行的状态:Task1还需1秒完成,Task2还需2秒完成,Task3还需3秒完成。 - 接下来执行
await a等待第一个任务完成,1秒后拿到返回值10;此时Task2已经运行了1秒,还剩1秒等待时间,Task3已经运行了1秒,还剩2秒等待时间。 - 接着执行
await b等待第二个任务完成,仅需再等1秒就拿到返回值10;此时Task3已经运行了2秒,还剩1秒等待时间。 - 最后执行
await c等待第三个任务完成,再等1秒拿到返回值10。
整个流程的总耗时取决于运行时间最长的Task3,也就是3秒,三个任务的等待时间是重叠的,属于并发执行。
内容的提问来源于stack exchange,提问作者murali krishnan
相关产品推荐
相关产品推荐

