在阅读了Loom proposal之后提出了这个问题,它描述了在Java编程语言中实现协同程序的方法.
特别是此提案表示,要在该语言中实现此功能,将需要额外的JVM支持.
据我所知,JVM上已经有多种语言可以将协同程序作为其功能集的一部分,例如Kotlin和Scala.
那么如何在没有额外支持的情况下实现此功能,如果没有它可以有效实施?
解决方法:
tl; dr摘要:
Particularly this proposal says that to implement this feature in the language the additional JVM support will be required.
当他们说“必需”时,他们的意思是“为了以一种在语言之间兼容和可互操作的方式实施”.
So how this feature is implemented without additional support
有许多方法,最容易理解它是如何工作的(但不一定最容易实现)是在JVM之上用你自己的语义实现自己的VM. (注意,这不是它实际完成的方式,这只是为什么可以完成它的直觉.)
and can it be implemented efficiently without it ?
并不是的.
稍微长一点的解释:
请注意,Project Loom的一个目标是将此抽象纯粹作为库引入.这有三个好处:
>引入新库比更改Java编程语言要容易得多.
> JVM可以使用每种语言编写的程序立即使用库,而Java程序只能使用Java语言功能.
>可以实现具有相同API但不使用新JVM功能的库,这将允许您通过简单的重新编译来编写在较旧JVM上运行的代码(尽管性能较低).
但是,将其实现为库可以排除聪明的编译器技巧,将协同例程转换为其他内容,因为不涉及编译器.如果没有聪明的编译器技巧,获得良好的性能将更加困难,因为它是JVM支持的“要求”.
更长的解释:
通常,所有通常的“强大”控制结构在计算意义上是等同的并且可以彼此使用.
最着名的那些“强大的”通用控制流结构是古老的GOTO,另一个是Continuations.然后,有线程和协同程序,以及人们通常不会想到的,但这也等同于GOTO:Exceptions.
一种不同的可能性是重新调用的调用堆栈,因此调用堆栈可以作为程序员的对象访问,并且可以被修改和重写. (例如,许多Smalltalk方言就是这样做的,它也很像C和汇编中的方式.)
只要你有其中一个,你就可以拥有所有这些,只需在另一个上面实现一个.
JVM有两个:Exceptions和GOTO,但JVM中的GOTO并不是通用的,它非常有限:它只能在单个方法中运行. (它主要用于循环.)因此,这给我们留下了例外.
因此,这是您的问题的一个可能的答案:您可以在例外之上实现协同例程.
另一种可能性是根本不使用JVM的控制流并实现自己的堆栈.
但是,这通常不是在JVM上实现协同例程时实际采用的路径.最有可能的是,实现协同例程的人会选择使用Trampolines并将执行上下文部分重新作为对象.也就是说,例如,如何在CLI上的C♯中实现生成器(不是JVM,但挑战类似). C♯中的生成器(基本上是受限制的半协同例程)是通过将方法的局部变量提升到上下文对象的字段中并在每个yield语句中将该方法拆分为该对象上的多个方法来实现的,将它们转换为状态机,并通过上下文对象上的字段仔细线程化所有状态更改.在async / await作为语言特性出现之前,一个聪明的程序员也使用相同的机制实现了异步编程.
然而,这就是你指出的文章最有可能提到的:所有这些机器都是昂贵的.如果你实现自己的堆栈或将执行上下文提升到一个单独的对象中,或者将所有方法编译成一个巨大的方法并在任何地方使用GOTO(由于方法的大小限制,甚至不可能),或者使用Exceptions作为控件-flow,这两件事中至少有一件是真的:
>您的调用约定与其他语言所期望的JVM堆栈布局不兼容,即您失去了互操作性.
> JIT编译器不知道你的代码到底在做什么,并且提供了字节代码模式,执行流模式和使用模式(例如抛出和捕获大量的异常),它没有预料到并且不知道如何优化,即你失去了表现.
Rich Hickey(Clojure的设计师)曾在一次演讲中说过:“Tail Calls,Performance,Interop.Pick Two.”我把它概括为我称之为Hickey的Maxim:“高级控制流,性能,互操作.选择两个.”
实际上,通常很难实现互操作或性能之一.
此外,您的编译器将变得更加复杂.
当构造在JVM中本地可用时,所有这一切都消失了.想象一下,例如,如果JVM没有Threads.然后,每个语言实现都会创建自己的Threading库,这个库很难,很复杂,很慢,并且不能与任何其他语言实现的Threading库互操作.
一个最近的,现实世界的例子是lambdas:JVM上的许多语言实现都有lambdas,例如:斯卡拉.然后Java也添加了lambdas,但由于JVM不支持lambda,它们必须以某种方式编码,而Oracle选择的编码与Scala之前选择的编码不同,这意味着你无法传递Java lambda期望Scala函数的Scala方法.这种情况下的解决方案是Scala开发人员完全重写了lambda的编码,以便与Oracle选择的编码兼容.这实际上在某些地方打破了向后兼容性.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。