Await /async vs. "classic"异步(回调)
本文关键字:quot 回调 异步 classic async Await vs | 更新日期: 2023-09-27 18:12:15
所以新的异步CTP非常酷;它使我的生活更容易,而不必编写命名的回调方法,并使方法的意图更清晰。
现在我已经玩了一点,我想知道async/await和"经典"异步回调语法之间可能有什么不同。
这是我脑子里的几个问题,但还有许多我现在没有想到的问题,可能以后会想到的。
- 一种可能比另一种提供更好的性能吗?
- 是否有一个开销大于另一个?
- 在高性能环境中使用哪个更好?
答案很复杂,当前编译器的await实现在某些方面比回调好,但在某些情况下更差。
。.NET执行上下文:我们打算使用await和ContinueWith(…)捕获和恢复。NET执行上下文。否则,它将无法通过。net安全要求,因为这样您就可以获取任意的东西,如凭据等,并将它们留在线程池中以供下一个工作项使用。对于'await',这是我们在内部构建中做的调整,但这是在我们制作了//BUILD开发者预览版之后。
内存分配:在几个方面,'await'在内存分配方面比手动回调要好。关键是,对于具有许多等待的函数,您真正生成的是相当于几个回调的函数。如果按线性执行顺序有5个等待,并且执行总是流向结束,那么等效的回调将需要5个回调。对于这5个回调中的每一个,都可以生成一个单独的lambda闭包对象和一个代表特定lambda的委托。在await的情况下,编译器知道你不会将委托对象用于其他任何事情。因此,整个方法共享一个闭包和一个委托,并使用一个内部状态机来跟踪您在方法中的位置。因此,在这种情况下,'await'分配更少的对象,这实际上可以加快程序的速度,因为太多的对象= GC必须花费更多的时间来确定哪些是活的/死的。
快捷方式 'Await'也有比回调更花哨的语义。在创建回调lambda的情况下,无论如何,编译器都被迫分配闭包和lambda的入口点委托。对于'await', await契约允许为已经"完成"的可等待对象提供更优化的代码路径。如果在await被求值之前,可等待对象说它已经"完成"了,那么语义只是将结果拉出的纯粹传递。这意味着编译器有机会延迟分配,直到您真正需要它,因此您永远不会支付闭包分配、委托分配或调度成本,除非您确实需要它。当前的Developer Preview编译器包括这些性能优化。
为perf交换危险如果你真的想绕过。net安全模型,你可以想象这样一种情况,如果你绝对相信你永远不需要捕获/还原上下文,你可以通过避免执行上下文包/还原来获得一点perf。然而,大多数。net的方法都是在幕后默默完成的,所以你真的需要知道哪些方法可以在没有它的情况下提供原始访问。. net的经验法则是,如果API在部分信任中可用(例如Silverlight),那么API在调用时肯定会捕获上下文,然后恢复它,如果它是一个将执行转移到其他地方的API(例如ContinueWith, QueueUserWorkItem(…)等)。如果你滚动你自己的线程池,只是排队委托,你可以绕过这个,但很可能你不需要它。
我个人建议使用await。这是更高的层次,这是你想要的。我们已经付出了相当多的努力,试图为这个版本调整它,我们可能会进一步调整它。基于回调的api将有更多的限制,因为编译器只能在它们开始违反语言规则之前调优这么多。方法中的await允许您拥有比回调更智能的闭包。和…Await比回调更直观地读取/使用:)
与匿名函数和迭代器一样,async
和await
关键字是语法糖。从技术意义上讲,它们的效率并不比同等的无糖版本更高或更低。