异步函数简介

一般指 async 修饰符声明得、可包含await表达式得方法或匿名函数。

声明方式

异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字。async可以出现在返回值之前的任何位置, 如下示例:

        async public static void GetInfoAsync()
{
//...
} public async static void GetInfoAsync()
{
//...
} public static async void GetInfoAsync()
{
//...
}

异步方法的返回类型

异步函数的返回类型只能为: void、Task、Task<TResult>。

Task<TResult>: 代表一个返回值T类型的操作。

Task: 代表一个无返回值的操作。

void: 为了和传统的事件处理程序兼容而设计。

await(等待)

await等待的是什么? 可以是一个异步操作(Task)、亦或者是具备返回值的异步操作(Task<TResult>)的值, 如下:

        public async static void GetInfoAsync()
{
await GetData(); // 等待异步操作, 无返回值
await GetData<int>(); //等待异步操作, 返回值 int
} static Task GetData()
{
//...
return null;
} static Task<T> GetData<T>(int a)
{
//...
return null;
}

注: await 最终操作的是一个值, 当然, 也可以是无值,  如上GetData() , 否则就是一个 Task<T>  如上:  GetData<T>()

await执行过程

TaskAwaiter 获取执行结果

一般而言, await等待的一个异步操作, 无论是具备返回值还是否, 那么最终都会获得该操作是否已完成、具备返回值得异步操作可以获取他得返回结果。

所以这个时候, TaskAwaiter出现了, 无论是Task、还是Task<TResult>操作, 都具备GetAwaiter() 方法。

用于获取改操作得状态、返回结果, 及部分操作, 如下TaskAwaiter结构:

    //
// 摘要:
// 提供等待异步任务完成的对象。
public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
//
// 摘要:
// 获取一个值,该值指示是否已完成的异步任务。
//
// 返回结果:
// true 如果任务已完成;否则为 false。
//
// 异常:
// T:System.NullReferenceException:
// System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
public bool IsCompleted { get; } //
// 摘要:
// 结束异步任务完成之前的等待。
//
// 异常:
// T:System.NullReferenceException:
// System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
//
// T:System.Threading.Tasks.TaskCanceledException:
// 任务已取消。
//
// T:System.Exception:
// 在完成的任务 System.Threading.Tasks.TaskStatus.Faulted 状态。
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public void GetResult();
//
// 摘要:
// 设置时应执行的操作 System.Runtime.CompilerServices.TaskAwaiter 对象停止等待异步任务完成。
//
// 参数:
// continuation:
// 要在等待操作完成时执行的操作。
//
// 异常:
// T:System.ArgumentNullException:
// continuation 为 null。
//
// T:System.NullReferenceException:
// System.Runtime.CompilerServices.TaskAwaiter 对象未正确初始化。
[SecuritySafeCritical]
public void OnCompleted(Action continuation);
//
// 摘要:
// 计划程序与此等待异步任务的延续任务操作。
//
// 参数:
// continuation:
// 要等待操作完成时调用的操作。
//
// 异常:
// T:System.ArgumentNullException:
// continuation 为 null。
//
// T:System.InvalidOperationException:
// 该等待程序未正确初始化。
[SecurityCritical]
public void UnsafeOnCompleted(Action continuation);
}

 接下来, 演示如何通过等待去获取异步操作的返回结果, 如下代码所示:

        public async static void GetInfoAsync()
{
Task<bool> task = Task.Run<bool>(() =>
{
Thread.Sleep(); //模拟耗时
return true;
}); //以下两种方式
bool taskResult1 = await task; //内部自己执行了GetAwaiter()
bool taskResult = task.GetAwaiter().GetResult(); //自己手动执行Awaiter(), 但是阻塞UI
       Console.WriteLine(taskResult);
}

注: 对于一个await表达式, 编译器生成的代码会先调用GetAwaiter(), 然后通过awaiter得成员来等待结果, 所以以上两种方式等效( 不考虑阻塞的情况下)

为了验证以上猜测, 通过反编译工具查看得到如下代码:

编译器最终生成两个密封类, 一个类( <>c )我们展开分析:

<GetInfoAsync>b__1_0()  正是模拟耗时的一个操作委托生成的方法。

        [CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <> = new Program.<>c();
public static Func<bool> <>9__1_0;
internal bool <GetInfoAsync>b__1_0()
{
Thread.Sleep();
return true;
}
}

第二个类 <GetInfoAsync>d__1 分析:

该类分别实现了接口  IAsyncStateMachine 的MoveNext() 与 SetStateMachine() ,另外 注意,

还特别定义了一个 <>t__builder, 先记住他, 下面讲会对他讲到, 为什么编译器生成的代码会默认先调用GetAwaiter()

 [CompilerGenerated]
private sealed class <GetInfoAsync>d__1 : IAsyncStateMachine
{
public int <>1__state;
public AsyncVoidMethodBuilder <>t__builder;
private Task<bool> <task>5__1;
private bool <result>5__2;
private bool <>s__3;
private TaskAwaiter<bool> <>u__1;
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter<bool> awaiter;
if (num != )
{
Func<bool> arg_2F_0;
if ((arg_2F_0 = Program.<>c.<>9__1_0) == null)
{
arg_2F_0 = (Program.<>c.<>9__1_0 = new Func<bool>(Program.<>c.<>.<GetInfoAsync>b__1_0));
}
this.<task>5__1 = Task.Run<bool>(arg_2F_0);
awaiter = this.<task>5__1.GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = ;
this.<>u__1 = awaiter;
Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Program.<GetInfoAsync>d__1>(ref awaiter, ref <GetInfoAsync>d__);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter<bool>);
this.<>1__state = -;
}
this.<>s__3 = awaiter.GetResult();
this.<result>5__2 = this.<>s__3;
Console.WriteLine(this.<result>5__2);
}
catch (Exception exception)
{
this.<>1__state = -;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}

接下来, 看GetInfoAsync()方法, 这个是自己编写的, 但是实现的细节,最终转换成了编译器执行代码:

        [AsyncStateMachine(typeof(Program.<GetInfoAsync>d__1)), DebuggerStepThrough]
public static void GetInfoAsync()
{
Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = new Program.<GetInfoAsync>d__1();
<GetInfoAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
<GetInfoAsync>d__.<>1__state = -;
AsyncVoidMethodBuilder <>t__builder = <GetInfoAsync>d__.<>t__builder;
<>t__builder.Start<Program.<GetInfoAsync>d__1>(ref <GetInfoAsync>d__); //注意到该代码, 调用了Start(),也许这就是默认实现的地方
}

通过查看Start泛型方法的实现, 最终找到了, 该泛型的条件限制于必须实现与 IAsyncStateMachine 接口, 所以通过查看, 该类最终调用了 MoveNext(), 而MoveNext中正

调用了GetAwaiter()。关于Start的实现如下所示:

[SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
if (stateMachine == null)
{
throw new ArgumentNullException("stateMachine");
}
ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
RuntimeHelpers.PrepareConstrainedRegions();
try
{
ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
stateMachine.MoveNext();
}
finally
{
executionContextSwitcher.Undo();
}
}

剖析MoveNext

对比IDE中的代码, 如下所示:

总结

await等待的是任务的操作值, 最终返回是异步操作的返回结果。而这一切都是因为编译器创建了一系列复杂的状态机制, 以达到其实现。

C# async/await异步编程深入理解的更多相关文章

  1. 【转】C# Async/Await 异步编程中的最佳做法

    Async/Await 异步编程中的最佳做法 Stephen Cleary 近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支 ...

  2. .NET Web应用中为什么要使用async/await异步编程

    前言 什么是async/await? await和async是.NET Framework4.5框架.C#5.0语法里面出现的技术,目的是用于简化异步编程模型. async和await的关系? asy ...

  3. C#中 Thread,Task,Async/Await 异步编程

    什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...

  4. async/await 异步编程

    前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在 ...

  5. Async await 异步编程说明

    希望在编程上有些许提高所以 最近连续2篇博客都在说明多线程和异步编程的使用,异步和多线程之间区别请自行百度,因为理解不是特别透彻就不在叙述以免误导大家,这里写下新研究整理 task  和 await ...

  6. async/await 异步编程(转载)

    转载地址:http://www.cnblogs.com/teroy/p/4015461.html 前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入 ...

  7. async & await 异步编程的一点巧方法

    await 关键字不会创建新的线程,而是由Task任务或是FCL中的xxxAsync等方法创建的线程,而且这里创建的线程都是基于线程池创建的工作线程,属于后台线程. await关键字会阻塞/暂停调用它 ...

  8. c# async/await异步编程死锁的问题

    在异步编程中,如果稍有不注意,就会造成死锁问题.何为死锁:即两个以上的线程同时争夺被互相锁住的资源,两个都不放手. 在UI或asp.net中,容易造成死锁的代码如下所示: private void b ...

  9. c# 关于async/await异步编程的浅析和使用

    线程的同步运行,时效性慢,异步运行,时效性快! 在c#5.0引出了async/await关键字,可以用其来进行异步编程. async/await定义异步方法的语法如下: 1.在方法的返回类型前面加上a ...

随机推荐

  1. poj_3468,线段树成段更新

    参考自http://www.notonlysuccess.com/index.php/segment-tree-complete/ #include<iostream> #include& ...

  2. xss  多分类 优选 贝叶斯、逻辑回归、决策树

    import re import numpy as np from sklearn import cross_validation from sklearn import datasets from ...

  3. Android5.0之后的页面切换动画

    Android5.0之后给我们开发者剩了好多的事情,为什么这么说呢?还记得刚开始的时候,Android里面的所有的动画都要我们开发者自己来写,现在不需要了,因为5.0之后自带了好多的动画,比如:按钮点 ...

  4. 机器学习(七) PCA与梯度上升法 (下)

    五.高维数据映射为低维数据 换一个坐标轴.在新的坐标轴里面表示原来高维的数据. 低维 反向 映射为高维数据 PCA.py import numpy as np class PCA: def __ini ...

  5. spinlock参考资料

    spinlock:http://irl.cs.ucla.edu/~yingdi/web/paperreading/smp_locking.pdf

  6. PostgreSQL Replication之第九章 与pgpool一起工作(1)

    在前面的章节中,我们已经能够深入地理解了pgbouncer,同时也学会了如何使用它来尽可能地优化复制设置.在本章我们将了解一个经常被称作与pgbouncer相对应的工具.尽管pgpool的思想与pgb ...

  7. TypeError: 'dict' object is not callabled

    Traceback (most recent call last): File "/root/Desktop/JuniperBackdoor-master/censys.py", ...

  8. Git 内部原理 - (5)引用规格 (6) 传输协议

    引用规格 纵观全书,我们已经使用过一些诸如远程分支到本地引用的简单映射方式,但这种映射可以更复杂. 假设你添加了这样一个远程版本库: $ git remote add origin https://g ...

  9. UCOSii任务就绪表之OSUnMapTbl[16*16]的数组是如何得到的

    我比较喜欢图,如下图: 图1: INT8U const OSUnMapTbl[]数组内的数据. 1.UCOSii的优先级相关内容 首先先介绍一个概念:优先级.UCOSii的优先级按倒叙排列,即优先级数 ...

  10. Java Web Application——servlet

    概述 是一个部署于web服务器中的实现了servlet接口的Java类,用于响应web请求 Web容器(也称为servlet容器)本质上是与servlet交互的Web服务器的组件.Web容器负责管理s ...