用Java实现协程
这个问题与我在Java中现有协程实现的问题有关。 如果,如我所怀疑的,事实证明,目前在Java中没有完全实现协程,那么实现它们需要什么?
正如我在那个问题中所说的,我知道以下几点:
我会依次解决每个人的缺陷。
基于线程的协程
这个“解决方案”是病态的。 协程的整个目标是避免线程,锁定,内核调度等的开销。协程本应该是轻快的,并且只能在用户空间中执行。 通过严格限制的全倾斜线程来实现它们可以摆脱所有优点。
JVM字节码操作
这个解决方案更实用,尽管有点难以实现。 这与C语言中的协程库(这是它们中的多少个可以工作)跳转到汇编语言大致相同,其优点是您只有一个架构需要担心和正确。
它还将您绑定到仅在完全兼容的JVM堆栈上运行代码(这意味着,例如,没有Android),除非您可以找到在非兼容堆栈上执行相同操作的方法。 但是,如果您确实找到了一种方法,那么您的系统复杂性和测试需求就会增加一倍。
达芬奇机器
达芬奇机器对于实验来说很酷,但由于它不是标准的JVM,因此它的功能无处不在。 事实上,我怀疑大多数生产环境都会特别禁止使用达芬奇机器。 因此,我可以用它做出很酷的实验,但不能用于任何我希望发布到现实世界的代码。
这也有类似于上面的JVM字节码操作解决方案的附加问题:不能在替代堆栈(如Android的)上工作。
JNI实现
这个解决方案使得在Java中进行这一切毫无意义。 CPU和操作系统的每个组合都需要进行独立测试,每个组合都有可能使微妙的故障受挫。 或者,当然,我可以将自己完全绑定到一个平台上,但这也使得Java中的事情完全没有实际意义。
所以...
有没有什么办法可以在不使用这四种技术之一的情况下用Java实现协程? 或者我会被迫使用那些味道最少(JVM操作)的四个之一呢?
编辑添加:
只是为了确保包含混淆,这是我的另一个相关问题,但不是相同的。 为了避免不必要的重复发明,该公司正在寻找现有的实施方案。 这个问题涉及到如果另一个证明无法回答的话,我们将如何执行Java中的协程。 目的是在不同的主题上保留不同的问题。
我会看看这个:http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html,它非常有趣,应该提供一个开始的好地方。 但是我们当然使用Java,所以我们可以做得更好(或者更糟,因为没有宏)
根据我对协程的理解,你通常有一个生产者和一个消费者协同程序(或者至少这是最常见的模式)。 但从语义上讲,你不希望制片人打电话给消费者,反之亦然,因为这会引起不对称。 但考虑到基于堆栈的语言的工作方式,我们需要有人来打电话。
所以这是一个非常简单的类型层次结构:
public interface CoroutineProducer<T>
{
public T Produce();
public boolean isDone();
}
public interface CoroutineConsumer<T>
{
public void Consume(T t);
}
public class CoroutineManager
{
public static Execute<T>(CoroutineProducer<T> prod, CoroutineConsumer<T> con)
{
while(!prod.IsDone()) // really simple
{
T d = prod.Produce();
con.Consume(d);
}
}
}
当然,最难的部分是实现接口,特别是难以将计算分解成单独的步骤。 为此,您可能需要一整套其他持久控制结构 。 其基本思想是我们想要模拟非局部控制转移(最终它的有点像我们模拟goto
)。 我们基本上希望通过将当前操作的状态保持在堆而不是堆栈pc
使用堆栈和pc
(程序计数器)。 因此,我们将需要一大堆帮助类。
例如:
假设在一个理想的世界中,你想写一个看起来像这样的消费者(psuedocode):
boolean is_done;
int other_state;
while(!is_done)
{
//read input
//parse input
//yield input to coroutine
//update is_done and other_state;
}
我们需要像is_done
和other_state
一样抽象局部变量,并且我们需要抽象while循环本身,因为我们的yield
类似于操作不会使用堆栈。 所以我们来创建一个while循环抽象和关联的类:
enum WhileState {BREAK, CONTINUE, YIELD}
abstract class WhileLoop<T>
{
private boolean is_done;
public boolean isDone() { return is_done;}
private T rval;
public T getReturnValue() {return rval;}
protected void setReturnValue(T val)
{
rval = val;
}
public T loop()
{
while(true)
{
WhileState state = execute();
if(state == WhileState.YIELD)
return getReturnValue();
else if(state == WhileState.BREAK)
{
is_done = true;
return null;
}
}
}
protected abstract WhileState execute();
}
这里的基本技巧是将局部变量移动为类变量,并将范围块转换为类,这使得我们能够在产生返回值后重新输入'循环'。
现在执行我们的制作人
public class SampleProducer : CoroutineProducer<Object>
{
private WhileLoop<Object> loop;//our control structures become state!!
public SampleProducer()
{
loop = new WhileLoop()
{
private int other_state;//our local variables become state of the control structure
protected WhileState execute()
{
//this implements a single iteration of the loop
if(is_done) return WhileState.BREAK;
//read input
//parse input
Object calcluated_value = ...;
//update is_done, figure out if we want to continue
setReturnValue(calculated_value);
return WhileState.YIELD;
}
};
}
public Object Produce()
{
Object val = loop.loop();
return val;
}
public boolean isDone()
{
//we are done when the loop has exited
return loop.isDone();
}
}
其他基本控制流程结构也可以采用类似的技巧。 你最好建立一个这些帮助类的库,然后用它们来实现这些简单的接口,最终将为你提供协程的语义。 我敢肯定,我在这里写的所有内容都可以概括并扩展。
我建议在JVM上看看Kotlin协同程序。 不过,它属于不同的类别。 没有涉及字节码操作,它也适用于Android。 但是,您将不得不在Kotlin中编写协程。 好处在于Kotlin专为与Java兼容而设计,因此您仍然可以继续使用所有的Java库,并在同一个项目中自由组合Kotlin和Java代码,甚至可以将它们并排放置在相同的目录中包。
这个kotlinx.coroutines指南提供了更多的例子,而协程设计文档解释了所有的动机,用例和实现细节。
我刚刚遇到了这个问题,只是想提一下,我认为可能以类似C#的方式实现协程或生成器。 这就是说我实际上并不使用Java,但CIL与JVM具有非常类似的限制。
C#中的yield语句是纯语言功能,不是CIL字节码的一部分。 C#编译器只是为每个生成器函数创建一个隐藏的私有类。 如果在函数中使用yield语句,它必须返回一个IEnumerator或一个IEnumerable。 编译器将您的代码“打包”到类似于状态机的类中。
C#编译器可能会在生成的代码中使用一些“goto's”来使转换成状态机变得更容易。 我不知道Java字节码的功能,如果有像普通的无条件跳转那样的功能,但在“汇编级”通常是可以的。
如前所述,该功能必须在编译器中实现。 因为我对Java只有很少的了解,而且它的编译器我不知道是否有可能改变/扩展编译器,也许使用“预处理器”或其他东西。
我个人喜欢协程。 作为一名Unity游戏开发者,我经常使用它们。 因为我用ComputerCraft玩很多Minecraft,我很好奇为什么Lua中的协程(LuaJ)是用线程来实现的。
链接地址: http://www.djcxy.com/p/53183.html