我已经在C#、Hack和Kotlin中看到了这一点:await或等效的操作只能在特殊的“异步”上下文中执行。这些返回值(借用Hack的术语)依次是"awaitable“,因此一些低级异步系统函数的特殊异步性会将气泡调用到顶部,除非它转换为同步操作。这将代码基划分为同步和异步生态系统。但是,在使用异步等待在Hack中工作了一段时间之后,我开始怀疑是否需要异步等待。为什么调用作用域需要知道它正在调用异步函数?为什么异步函数不能看起来像同步函数,只是偶尔在其他地方抛出控制呢?
我发现了我编写的异步代码的所有独特性来自于三个后果:
我承认第一个很诱人。用async的尖叫声注释一个生态系统:“当心:种族条件可能就住在这里!”然而,对竞赛条件的关注可以完全局限于组合函数(例如(Awaitable<Tu>, Awaitable<Tv>, ...) -> Awaitable<(Tu, Tv, ...)>),因为没有它们,两个协同线就不能并行执行。然后,问题变得非常具体:“确保这个组合函数的所有术语都不竞争。”这有利于澄清问题。只要理解组合函数对于异步代码是有用的(但显然不限于它;异步代码是一组超同步代码),并且有有限数量的规范代码(语言结构通常是这样的),我觉得通过本地化它们的源来更好地沟通竞争条件的风险。
另外两个是语言设计问题,具体是如何表示最低级别的异步对象(例如,哈克的WaitHandles )。任何高级异步对象的突变都必然仅限于针对底层低级异步对象的一组操作,这些对象来自系统调用。调用范围是否同步是不相关的,因为在单个时间点,可变性和突变的影响都是该底层状态的纯函数。将它们聚合到一个无法描述的异步对象中并不能使行为变得更清楚--如果说有什么区别的话,对我来说,它用决定论的错觉掩盖了它。当调度程序是不透明的(就像在Hack中,据我所收集到的,Kotlin也是如此),这一切都是没有意义的,因为信息和变异程序都是隐藏的。
否则,调用范围的结果是相同的:它最终得到一个值或一个异常,并执行其同步操作。我是不是遗漏了这个规则背后的设计思想的一部分?或者,是否有异步函数契约与同步函数契约不可区分的例子?
发布于 2017-03-23 02:49:51
我觉得你看得太多了。async更改函数的返回类型。我不知道C#如何或者是否表示超出了async,但是在Scala中(我相信Kotlin,但我对它不太熟悉),您的返回类型实际上是从A到Future[A]。显然,这意味着调用上下文必须生成不同的代码来检索返回值。
在Scala中,将隐式转换从A添加到Future[A] (在调用上下文中需要Future[A] )并不困难,只需将其封装在async {}块中即可。同样,在Await.result调用中添加隐式转换以转到另一个方向是非常简单的。其他语言可以很容易地在编译器中完成这种转换。
但是,这将是一个错误,因为您放弃了类型检查器在编写异步代码和精确控制希望它成为同步点方面所提供的所有帮助。编译器强迫您将所有异步代码保存在async块中,因此当您意外地比调用堆栈中的目标更深地同步时,它会给您一个有用的错误消息。类型越弱,保护就越弱。
换句话说,这基本上是您不希望编译器自动将字符串转换为整数的原因。在处理像异步代码这样容易出错的事情时,您希望编译器尽可能多地帮助您。
发布于 2017-03-24 07:22:20
异步和等待只是语法上的糖。您不需要使用它们,但是如果您使用它们,代码会更干净、更易读。
当您将函数标记为异步时,编译器会注入一堆额外的代码。您可以自己编写该代码,但它总是遵循相同的模式,在阅读代码时可能会分散注意力。
https://softwareengineering.stackexchange.com/questions/344726
复制相似问题