所以新的异步CTP非常酷;它使我的生活变得更加容易,不必编写命名的回调方法,并使方法的意图更加清晰。
现在我已经开始玩了一下,我想知道async / await和“经典”异步回调语法之间可能存在什么差异。
以下是我想到的一些问题,但还有很多其他问题我现在也不会想到,可能会在以后考虑。
答案 0 :(得分:14)
答案很复杂,await的当前编译器实现在几个方面比回调更好,但在某些情况下更糟糕。
.NET执行上下文:我们打算await和ContinueWith(...)捕获并恢复.NET执行上下文。否则它不会通过.NET安全要求,因为那时你可以采取任意的东西,如凭证等,并将它们留在线程池中,用于下一个工作项。对于'await',这是我们在内部构建中进行的调整,但是在我们生成// BUILD开发人员预览之后。
内存分配:在几种方式中,'await'在内存分配方面比手动回调更好。关键是对于有许多等待的函数,你真正生成的是相当于几个回调。如果你有5个等待线性执行顺序,并且执行总是流到最后,那么等价物将需要5个回调。对于这5个回调中的每一个,都可以生成一个单独的lambda闭包对象和一个代表该特定lambda的委托。在'await'的情况下,编译器知道你不会将委托对象用于其他任何事情。因此,整个方法共享1个闭包和1个委托,并使用内部状态机来跟踪您在方法中的位置。因此,对于这种情况,'await'会分配更少的对象,这实际上可以加速你的程序,因为太多的对象= GC必须花费更多的时间来计算出活着/死的东西。
Short-cutting 'Await'也具有比回调更精彩的语义。在您创建回调lambda的情况下,无论如何,编译器都被强制分配闭包和lambda的入口点委托。对于'await',await合同允许为已经“完成”的等待事件提供更优化的代码路径。如果awa在await得到评估之前说它已经“完成”了,那么语义只是将结果拉出来的纯粹传递。这意味着编译器有机会延迟分配,直到您确实需要它为止,因此您永远不会支付闭包分配,委托分配和调度成本,除非您确实需要它。当前的Developer Preview编译器包括这些性能优化。
为perf交易危险如果您真的想绕过.NET安全模型,您可以想象一下,通过避免执行上下文包/恢复可以获得一点性能,如果您绝对相信您永远不需要捕获/恢复上下文。但是,大多数.NET的方法都会默默地执行这些操作,因此您确实需要知道哪些方法可以在没有它的情况下为您提供原始访问权限。 .NET的经验法则是,如果API在部分信任(例如Silverlight)中可用,那么API在调用时肯定会捕获上下文,然后恢复它,如果它是在其他地方传输执行的API(例如ContinueWith,QueueUserWorkItem( ......)等)。如果你推出自己的线程池,只是排队代表,你可以绕过这个,但很可能你不需要它。
我的个人推荐使用等待。这是更高的水平,这是你想要的。我们已经付出了相当大的努力来尝试为此版本调整它,我们可能会进一步调整它。基于回调的API将更具限制性,因为编译器在开始破坏语言规则之前只能调整太多。等待一种方法允许你拥有比回调更聪明的闭包。 AND ... await比回调更容易阅读/使用:)
答案 1 :(得分:4)
与匿名函数和迭代器一样,async
和await
关键字是语法糖。从技术意义上讲,它们的效率不低于等效的非含糖版本。他们只是为你节省了很多打字。