异步函数简介
一般指 async 修饰符声明得、可包含await表达式得方法或匿名函数。
声明方式
异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字。async可以出现在返回值之前的任何位置, 如下示例:
async public static void getinfoasync() { //... } public async static void getinfoasync() { //... } public static async void getinfoasync() { //... }
异步方法的返回类型
异步函数的返回类型只能为: void、task、task
task
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
await执行过程
taskawaiter 获取执行结果
一般而言, await等待的一个异步操作, 无论是具备返回值还是否, 那么最终都会获得该操作是否已完成、具备返回值得异步操作可以获取他得返回结果。
所以这个时候,taskawaiter出现了, 无论是task、还是task
用于获取改操作得状态、返回结果, 及部分操作, 如下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() { tasktask = 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 )我们展开分析:
[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; } }
第二个类
该类分别实现了接口 iasyncstatemachine 的movenext() 与 setstatemachine() ,另外 注意,
还特别定义了一个 <>t__builder, 先记住他, 下面讲会对他讲到, 为什么编译器生成的代码会默认先调用getawaiter()
[compilergenerated] private sealed classd__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.start d__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用法详解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。