1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > C# async/await异步编程深入理解

C# async/await异步编程深入理解

时间:2023-03-14 00:40:04

相关推荐

C# async/await异步编程深入理解

异步函数简介

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

声明方式

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

asyncpublic static void GetInfoAsync(){//... }publicasyncstatic void GetInfoAsync(){//... }public staticasyncvoid 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{//// 摘要://获取一个值,该值指示是否已完成的异步任务。//// 返回结果://true 如果任务已完成;否则为 false。//// 异常:// T:System.NullReferenceException://pilerServices.TaskAwaiter 对象未正确初始化。public bool IsCompleted { get; }//// 摘要://结束异步任务完成之前的等待。//// 异常:// T:System.NullReferenceException://pilerServices.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();//// 摘要://设置时应执行的操作 pilerServices.TaskAwaiter 对象停止等待异步任务完成。//// 参数:// continuation://要在等待操作完成时执行的操作。//// 异常:// T:System.ArgumentNullException://continuation 为 null。//// T:System.NullReferenceException://pilerServices.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()

1 [CompilerGenerated] 2 private sealed class <GetInfoAsync>d__1 : IAsyncStateMachine 3 { 4 public int <>1__state; 5 public AsyncVoidMethodBuilder <>t__builder; 6 private Task<bool> <task>5__1; 7 private bool <result>5__2; 8 private bool <>s__3; 9 private TaskAwaiter<bool> <>u__1;10 void IAsyncStateMachine.MoveNext()11 {12 int num = this.<>1__state;13 try14 {15 TaskAwaiter<bool> awaiter;16 if (num != 0)17 {18Func<bool> arg_2F_0;19if ((arg_2F_0 = Program.<>c.<>9__1_0) == null)20{21 arg_2F_0 = (Program.<>c.<>9__1_0 = new Func<bool>(Program.<>c.<>9.<GetInfoAsync>b__1_0));22}23this.<task>5__1 = Task.Run<bool>(arg_2F_0);24awaiter = this.<task>5__1.GetAwaiter();25if (!awaiter.IsCompleted)26{27 this.<>1__state = 0;28 this.<>u__1 = awaiter;29 Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = this;30 this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Program.<GetInfoAsync>d__1>(ref awaiter, ref <GetInfoAsync>d__);31 return;32}33 }34 else35 {36awaiter = this.<>u__1;37this.<>u__1 = default(TaskAwaiter<bool>);38this.<>1__state = -1;39 }40 this.<>s__3 = awaiter.GetResult();41 this.<result>5__2 = this.<>s__3;42 Console.WriteLine(this.<result>5__2);43 }44 catch (Exception exception)45 {46 this.<>1__state = -2;47 this.<>t__builder.SetException(exception);48 return;49 }50 this.<>1__state = -2;51 this.<>t__builder.SetResult();52 }53 [DebuggerHidden]54 void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)55 {56 }57 }

View Code

接下来, 看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 : 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等待的是任务的操作值, 最终返回是异步操作的返回结果。而这一切都是因为编译器创建了一系列复杂的状态机制, 以达到其实现。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。