C#异步编程async/await用法详解

本文详细讲解了C#异步编程async/await的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

异步函数简介

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

声明方式

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

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

异步方法的返回类型

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

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

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

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

await(等待)

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

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

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

await执行过程

TaskAwaiter 获取执行结果

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

所以这个时候,TaskAwaiter出现了, 无论是Task、还是Task操作, 都具备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 task = Task.Run(() => { Thread.Sleep(10000); //模拟耗时 return true; }); //以下两种方式 bool taskResult1 = await task;  //内部自己执行了GetAwaiter() bool taskResult = task.GetAwaiter().GetResult();  //自己手动执行Awaiter(), 但是阻塞UI        Console.WriteLine(taskResult); }

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

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

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

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

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

第二个类d__1 分析:

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

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

 [CompilerGenerated] private sealed class d__1 : IAsyncStateMachine { public int <>1__state; public AsyncVoidMethodBuilder <>t__builder; private Task 5__1; private bool 5__2; private bool <>s__3; private TaskAwaiter <>u__1; void IAsyncStateMachine.MoveNext() { int num = this.<>1__state; try { TaskAwaiter awaiter; if (num != 0) { Func arg_2F_0; if ((arg_2F_0 = Program.<>c.<>9__1_0) == null) { arg_2F_0 = (Program.<>c.<>9__1_0 = new Func(Program.<>c.<>9.b__1_0)); } this.5__1 = Task.Run(arg_2F_0); awaiter = this.5__1.GetAwaiter(); if (!awaiter.IsCompleted) { this.<>1__state = 0; this.<>u__1 = awaiter; Program.d__1 d__ = this; this.<>t__builder.AwaitUnsafeOnCompleted, Program.d__1>(ref awaiter, ref d__); return; } } else { awaiter = this.<>u__1; this.<>u__1 = default(TaskAwaiter); this.<>1__state = -1; } this.<>s__3 = awaiter.GetResult(); this.5__2 = this.<>s__3; Console.WriteLine(this.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.d__1)), DebuggerStepThrough] public static void GetInfoAsync() { Program.d__1 d__ = new Program.d__1(); d__.<>t__builder = AsyncVoidMethodBuilder.Create(); d__.<>1__state = -1; AsyncVoidMethodBuilder <>t__builder = d__.<>t__builder; <>t__builder.Startd__1>(ref d__); //注意到该代码, 调用了Start(),也许这就是默认实现的地方 }

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

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

[SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable] public void Start(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用法详解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持0133技术站。

以上就是C#异步编程async/await用法详解的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 其他教程