C#异步编程asyncawait⽤法详解
异步函数简介
⼀般指 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>(1); //等待异步操作, 返回值 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
{
//
// 摘要:
//    获取⼀个值,该值指⽰是否已完成的异步任务。
//
// 返回结果:
typeof的用法
/
/    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(10000); //模拟耗时
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 <>9 = new Program.<>c();
public static Func<bool> <>9__1_0;
internal bool <GetInfoAsync>b__1_0()
{
Thread.Sleep(10000);
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 != 0)
{
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.<>9.<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 = 0;
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 = -1;
}
this.<>s__3 = awaiter.GetResult();
this.<result>5__2 = this.<>s__3;
Console.WriteLine(this.<result>5__2);
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
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 = -1;
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 : IAsyncS
tateMachine
{
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中的代码, 如下所⽰:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。