用Java实现协程


71

这个问题与我有关Java中现有协程实现的问题有关。如我所怀疑的,如果事实证明Java中没有可用的协程的完整实现,那么实现它们将需要什么?

正如我在这个问题中所说的,我了解以下内容:

  1. 您可以将“协程”实现为后台的线程/线程池。
  2. 您可以在后台使用JVM字节码来完成棘手的事情,以使协程成为可能。
  3. 所谓的“达芬奇机” JVM实现具有使协程在无需字节码操作的情况下就可以实现的原语。
  4. 也有多种基于JNI的协程方法。

我将依次解决每个人的不足之处。

基于线程的协程

这种“解决方案”是病态的。协程的全部目的是避免线程,锁定,内核调度等开销。协程应该轻巧,快速,并且只能在用户空间中执行。根据具有严格限制的全倾斜线程来实现它们会失去所有优势。

JVM字节码操作

此解决方案更实用,尽管很难实现。这大致相当于跳入C语言中的协程库的汇编语言(这是其中的许多工作原理),其优点是您只需要担心一种架构即可。

除非您找到在不兼容的JVM堆栈上执行相同操作的方法,否则还将使您只能在完全兼容的JVM堆栈(例如,没有Android)上运行代码。但是,如果确实找到了解决方法,那么您现在已经将系统复杂性和测试需求提高了一倍。

达芬奇机

达芬奇机很酷,可以进行实验,但是由于它不是标准的JVM,因此它的功能不会在任何地方都可用。实际上,我怀疑大多数生产环境都将特别禁止使用达芬奇机器。因此,我可以使用它来进行出色的实验,但不能用于我希望发布到现实世界中的任何代码。

这也具有类似于上面的JVM字节码操作解决方案的附加问题:在替代堆栈(如Android的堆栈)上将不起作用。

JNI实施

这种解决方案完全没有用Java来做到这一点。CPU和操作系统的每种组合都需要独立的测试,并且每一个都是潜在的令人沮丧的细微故障点。另外,当然,我可以将自己完全束缚在一个平台上,但这也使得在Java中做事情完全没有意义。

所以...

是否可以在不使用这四种技术之一的情况下用Java实现协程?还是我会被迫使用气味最小的那四种(JVM操作)中的一种?


编辑添加:

只是为了确保避免混淆,这是我的另一个相关的问题,但并非相同。为了避免不必要地重新发明轮子,那个人正在寻找现有的实现。这个问题涉及一个问题,即如果另一个问题无法解决,如何在Java中实现协程。目的是在不同的线程上保留不同的问题。



25
@Matthew富兰琛:看第一句这一个。请。我的意思是我阻止了“可能重复”的链接,并解释了“非常下一句话”中的不同之处。
只是我的正确观点,2010年

3
我已经阅读了两个问题,而且我知道它们并不完全相同。但是,它们之间的联系足够紧密,我认为您应该将它们合并为一个。考虑到自您问第一个小时以来只有一个小时,这尤其正确。
马修·弗拉申

8
我不同意。两者答案的性质差异很大,它们属于不同的问题。一种是简单的“这是一个满足您需求的库”式的答案。另一个是“您可以按照自己的意愿去做”的答案。简而言之,一个是响应的URL,另一个是(伪)代码。他们是完全不同种类的问题和共享只有两个字的答案,真是:“Java的”和“协同程序”。
只是我的正确观点,2010年

1
实际上,这可能没有什么区别。国际海事组织,您不太可能对这两个问题有很多答案。您的要求似乎排除了所有可行的实施策略AFAIK。
斯蒂芬·C

Answers:


42

我来看一下: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通过将当前操作的状态保留在堆中而不是堆栈中来摆脱使用堆栈和(程序计数器)的麻烦。因此,我们将需要一堆帮助程序类。

例如:

假设在一个理想的世界中,您想要编写一个看起来像这样的使用者(伪代码):

boolean is_done;
int other_state;
while(!is_done)
{
    //read input
    //parse input
    //yield input to coroutine
    //update is_done and other_state;
}

我们需要抽象局部变量likeis_doneother_state,并且需要抽象while循环本身,因为我们的yieldlike操作不会使用堆栈。因此,让我们创建一个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();
    }
}

对于其他基本控制流结构,可以采取类似的技巧。理想情况下,您将建立一个包含这些帮助器类的库,然后使用它们来实现这些简单的接口,这些接口最终将为您提供协同例程的语义。我确信我在这里编写的所有内容都可以得到概括和扩展。


我实际上对该特定文章非常熟悉。;)我使用了很长一段时间,直到我出于C语言的协程需求而切换到libpcl为止。我现在必须立即着手工作,因此当我回来时,我将仔细研究一下该代码到Java的翻译。 。感谢你的回答。
我的正确观点

我不会说这是该代码的翻译,由于我们没有goto或duffs设备或宏,因此基本上不可能做到这一点,但是该链接对于准确了解实现问题是非常有用的。
路加福音

3
多语言圈子里有一句老话:“翻译诗歌是不可能的,但总值得付出努力。” 我在代码上采取相同的立场。您无法翻译代码惯用语,但这是值得的。通过向我展示该代码在Java中的外观,您基本上已经回答了我的问题:我必须使用四种有臭味的技术之一,因为仅在Java中这是不可能的。为此,您将获得选定的答案。(好吧,这是唯一的答案。:D)
我的正确观点是2010年

11

我建议看看JVM上的Kotlin协程。但是,它属于不同的类别。不涉及字节码操作,它也适用于Android。但是,您将不得不在Kotlin中编写协程。好处是Kotlin是为与Java互操作而设计的,因此您仍然可以继续使用所有Java库,并在同一项目中自由组合Kotlin和Java代码,甚至可以将它们并排放置在同一目录中,包。

kotlinx.coroutines指南提供了更多示例,而coroutines设计文档则说明了所有动机,用例和实现细节。


4

我只是碰到了这个问题,只想提一下,我认为以C#的相似方式实现协程或生成器是可能的。那就是说我实际上并没有使用Java,但是CIL具有与JVM非常相似的限制。

C#中的yield语句是纯语言功能,不属于CIL字节码。C#编译器仅为每个生成器函数创建一个隐藏的私有类。如果在函数中使用yield语句,则必须返回IEnumerator或IEnumerable。编译器将您的代码“打包”成类似状态机的类。

C#编译器可能会在生成的代码中使用一些“ goto's”来简化向状态机的转换。我不知道Java字节码的功能,是否有像无条件的普通跳转那样的事情,但是在“汇编级别”通常是可能的。

如前所述,此功能必须在编译器中实现。因为我对Java和它的编译器知之甚少,所以我无法确定是否有可能使用“预处理器”之类的东西来更改/扩展编译器。

我个人很喜欢协程。作为Unity游戏开发人员,我经常使用它们。因为我在ComputerCraft上玩过很多Minecraft,所以很好奇为什么Lua(LuaJ)中的协程是用线程实现的。


有点晚,但是Lua本身的协程本身并没有使用线程实现,尽管它们可能在LuaJ中。
立方

4

Kotlin对协同例程使用以下方法
(来自https://kotlinlang.org/docs/reference/coroutines.html):

协程通过编译技术完全实现(不需要VM或OS方面的支持),而挂起则通过代码转换来实现。基本上,每个挂起函数(可能会进行优化,但此处不再赘述)都转换为状态机,其中状态对应于挂起调用。在暂停之前,下一个状态与相关的局部变量等一起存储在编译器生成的类的字段中。恢复该协程后,将恢复局部变量,并且状态机将从暂停后的状态继续进行。

悬挂的协程可以作为保持其悬挂状态和局部状态的对象进行存储和传递。此类对象的类型为Continuation,此处描述的总体代码转换与经典的Continuation-passing样式相对应。因此,悬挂功能在引擎盖下采用了Continuation类型的额外参数。

https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md查看设计文档


2

Oracle上还有Java的Quasar和Oracle的Project Loom,它们在JVM上对光纤和延续进行了扩展。这是Loom在Youtoube上的演示。还有更多。稍加搜索即可轻松找到。


0

我有一个在Java中使用的Coroutine类。它基于线程,使用线程具有允许并行操作的优势,这在多核计算机上可能是一个优势。因此,您可能需要考虑基于线程的方法。


5
从这个问题开始:“协程的全部目的是避免线程,锁定,内核调度等开销。”
只是我的正确意见,2012年

0

Java6 +还有另外一个选择

pythonic协程实现:

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

class CorRunRAII {
    private final List<WeakReference<? extends CorRun>> resources = new ArrayList<>();

    public CorRunRAII add(CorRun resource) {
        if (resource == null) {
            return this;
        }
        resources.add(new WeakReference<>(resource));

        return this;
    }

    public CorRunRAII addAll(List<? extends CorRun> arrayList) {
        if (arrayList == null) {
            return this;
        }
        for (CorRun corRun : arrayList) {
            add(corRun);
        }

        return this;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        for (WeakReference<? extends CorRun> corRunWeakReference : resources) {
            CorRun corRun = corRunWeakReference.get();
            if (corRun != null) {
                corRun.stop();
            }
        }
    }
}

class CorRunYieldReturn<ReceiveType, YieldReturnType> {
    public final AtomicReference<ReceiveType> receiveValue;
    public final LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue;

    CorRunYieldReturn(AtomicReference<ReceiveType> receiveValue, LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue) {
        this.receiveValue = receiveValue;
        this.yieldReturnValue = yieldReturnValue;
    }
}

interface CorRun<ReceiveType, YieldReturnType> extends Runnable, Callable<YieldReturnType> {
    boolean start();
    void stop();
    void stop(final Throwable throwable);
    boolean isStarted();
    boolean isEnded();
    Throwable getError();

    ReceiveType getReceiveValue();
    void setResultForOuter(YieldReturnType resultForOuter);
    YieldReturnType getResultForOuter();

    YieldReturnType receive(ReceiveType value);
    ReceiveType yield();
    ReceiveType yield(YieldReturnType value);
    <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another);
    <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another, final TargetReceiveType value);
}

abstract class CorRunSync<ReceiveType, YieldReturnType> implements CorRun<ReceiveType, YieldReturnType> {

    private ReceiveType receiveValue;
    public final List<WeakReference<CorRun>> potentialChildrenCoroutineList = new ArrayList<>();

    // Outside

    private AtomicBoolean isStarted = new AtomicBoolean(false);
    private AtomicBoolean isEnded = new AtomicBoolean(false);
    private Throwable error;

    private YieldReturnType resultForOuter;

    @Override
    public boolean start() {

        boolean isStarted = this.isStarted.getAndSet(true);
        if ((! isStarted)
                && (! isEnded())) {
            receive(null);
        }

        return isStarted;
    }

    @Override
    public void stop() {
        stop(null);
    }

    @Override
    public void stop(Throwable throwable) {
        isEnded.set(true);
        if (throwable != null) {
            error = throwable;
        }

        for (WeakReference<CorRun> weakReference : potentialChildrenCoroutineList) {
            CorRun child = weakReference.get();
            if (child != null) {
                child.stop();
            }
        }
    }

    @Override
    public boolean isStarted() {
        return isStarted.get();
    }

    @Override
    public boolean isEnded() {
        return isEnded.get();
    }

    @Override
    public Throwable getError() {
        return error;
    }

    @Override
    public ReceiveType getReceiveValue() {
        return receiveValue;
    }

    @Override
    public void setResultForOuter(YieldReturnType resultForOuter) {
        this.resultForOuter = resultForOuter;
    }

    @Override
    public YieldReturnType getResultForOuter() {
        return resultForOuter;
    }

    @Override
    public synchronized YieldReturnType receive(ReceiveType value) {
        receiveValue = value;

        run();

        return getResultForOuter();
    }

    @Override
    public ReceiveType yield() {
        return yield(null);
    }

    @Override
    public ReceiveType yield(YieldReturnType value) {
        resultForOuter = value;
        return receiveValue;
    }

    @Override
    public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(CorRun<TargetReceiveType, TargetYieldReturnType> another) {
        return yieldFrom(another, null);
    }

    @Override
    public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(CorRun<TargetReceiveType, TargetYieldReturnType> another, TargetReceiveType value) {
        if (another == null || another.isEnded()) {
            throw new RuntimeException("Call null or isEnded coroutine");
        }

        potentialChildrenCoroutineList.add(new WeakReference<CorRun>(another));

        synchronized (another) {
            boolean isStarted = another.start();
            boolean isJustStarting = ! isStarted;
            if (isJustStarting && another instanceof CorRunSync) {
                return another.getResultForOuter();
            }

            return another.receive(value);
        }
    }

    @Override
    public void run() {
        try {
            this.call();
        }
        catch (Exception e) {
            e.printStackTrace();

            stop(e);
            return;
        }
    }
}

abstract class CorRunThread<ReceiveType, YieldReturnType> implements CorRun<ReceiveType, YieldReturnType> {

    private final ExecutorService childExecutorService = newExecutorService();
    private ExecutorService executingOnExecutorService;

    private static final CorRunYieldReturn DUMMY_COR_RUN_YIELD_RETURN = new CorRunYieldReturn(new AtomicReference<>(null), new LinkedBlockingDeque<AtomicReference>());

    private final CorRun<ReceiveType, YieldReturnType> self;
    public final List<WeakReference<CorRun>> potentialChildrenCoroutineList;
    private CorRunYieldReturn<ReceiveType, YieldReturnType> lastCorRunYieldReturn;

    private final LinkedBlockingDeque<CorRunYieldReturn<ReceiveType, YieldReturnType>> receiveQueue;

    // Outside

    private AtomicBoolean isStarted = new AtomicBoolean(false);
    private AtomicBoolean isEnded = new AtomicBoolean(false);
    private Future<YieldReturnType> future;
    private Throwable error;

    private final AtomicReference<YieldReturnType> resultForOuter = new AtomicReference<>();

    CorRunThread() {
        executingOnExecutorService = childExecutorService;

        receiveQueue = new LinkedBlockingDeque<>();
        potentialChildrenCoroutineList = new ArrayList<>();

        self = this;
    }

    @Override
    public void run() {
        try {
            self.call();
        }
        catch (Exception e) {
            stop(e);
            return;
        }

        stop();
    }

    @Override
    public abstract YieldReturnType call();

    @Override
    public boolean start() {
        return start(childExecutorService);
    }

    protected boolean start(ExecutorService executorService) {
        boolean isStarted = this.isStarted.getAndSet(true);
        if (!isStarted) {
            executingOnExecutorService = executorService;
            future = (Future<YieldReturnType>) executingOnExecutorService.submit((Runnable) self);
        }
        return isStarted;
    }

    @Override
    public void stop() {
        stop(null);
    }

    @Override
    public void stop(final Throwable throwable) {
        if (throwable != null) {
            error = throwable;
        }
        isEnded.set(true);

        returnYieldValue(null);
        // Do this for making sure the coroutine has checked isEnd() after getting a dummy value
        receiveQueue.offer(DUMMY_COR_RUN_YIELD_RETURN);

        for (WeakReference<CorRun> weakReference : potentialChildrenCoroutineList) {
            CorRun child = weakReference.get();
            if (child != null) {
                if (child instanceof CorRunThread) {
                    ((CorRunThread)child).tryStop(childExecutorService);
                }
            }
        }

        childExecutorService.shutdownNow();
    }

    protected void tryStop(ExecutorService executorService) {
        if (this.executingOnExecutorService == executorService) {
            stop();
        }
    }

    @Override
    public boolean isEnded() {
        return isEnded.get() || (
                future != null && (future.isCancelled() || future.isDone())
                );
    }

    @Override
    public boolean isStarted() {
        return isStarted.get();
    }

    public Future<YieldReturnType> getFuture() {
        return future;
    }

    @Override
    public Throwable getError() {
        return error;
    }

    @Override
    public void setResultForOuter(YieldReturnType resultForOuter) {
        this.resultForOuter.set(resultForOuter);
    }

    @Override
    public YieldReturnType getResultForOuter() {
        return this.resultForOuter.get();
    }

    @Override
    public YieldReturnType receive(ReceiveType value) {

        LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue = new LinkedBlockingDeque<>();

        offerReceiveValue(value, yieldReturnValue);

        try {
            AtomicReference<YieldReturnType> takeValue = yieldReturnValue.take();
            return takeValue == null ? null : takeValue.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public ReceiveType yield() {
        return yield(null);
    }

    @Override
    public ReceiveType yield(final YieldReturnType value) {
        returnYieldValue(value);

        return getReceiveValue();
    }

    @Override
    public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another) {
        return yieldFrom(another, null);
    }

    @Override
    public <TargetReceiveType, TargetYieldReturnType> TargetYieldReturnType yieldFrom(final CorRun<TargetReceiveType, TargetYieldReturnType> another, final TargetReceiveType value) {
        if (another == null || another.isEnded()) {
            throw new RuntimeException("Call null or isEnded coroutine");
        }

        boolean isStarted = false;
        potentialChildrenCoroutineList.add(new WeakReference<CorRun>(another));

        synchronized (another) {
            if (another instanceof CorRunThread) {
                isStarted = ((CorRunThread)another).start(childExecutorService);
            }
            else {
                isStarted = another.start();
            }

            boolean isJustStarting = ! isStarted;
            if (isJustStarting && another instanceof CorRunSync) {
                return another.getResultForOuter();
            }

            TargetYieldReturnType send = another.receive(value);
            return send;
        }
    }

    @Override
    public ReceiveType getReceiveValue() {

        setLastCorRunYieldReturn(takeLastCorRunYieldReturn());

        return lastCorRunYieldReturn.receiveValue.get();
    }

    protected void returnYieldValue(final YieldReturnType value) {
        CorRunYieldReturn<ReceiveType, YieldReturnType> corRunYieldReturn = lastCorRunYieldReturn;
        if (corRunYieldReturn != null) {
            corRunYieldReturn.yieldReturnValue.offer(new AtomicReference<>(value));
        }
    }

    protected void offerReceiveValue(final ReceiveType value, LinkedBlockingDeque<AtomicReference<YieldReturnType>> yieldReturnValue) {
        receiveQueue.offer(new CorRunYieldReturn(new AtomicReference<>(value), yieldReturnValue));
    }

    protected CorRunYieldReturn<ReceiveType, YieldReturnType> takeLastCorRunYieldReturn() {
        try {
            return receiveQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }

    protected void setLastCorRunYieldReturn(CorRunYieldReturn<ReceiveType,YieldReturnType> lastCorRunYieldReturn) {
        this.lastCorRunYieldReturn = lastCorRunYieldReturn;
    }

    protected ExecutorService newExecutorService() {
        return Executors.newCachedThreadPool(getThreadFactory());
    }

    protected ThreadFactory getThreadFactory() {
        return new ThreadFactory() {
            @Override
            public Thread newThread(final Runnable runnable) {
                Thread thread = new Thread(runnable);
                thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                    @Override
                    public void uncaughtException(Thread thread, Throwable throwable) {
                        throwable.printStackTrace();
                        if (runnable instanceof CorRun) {
                            CorRun self = (CorRun) runnable;
                            self.stop(throwable);
                            thread.interrupt();
                        }
                    }
                });
                return thread;
            }
        };
    }
}

现在您可以通过这种方式使用python协程(例如斐波那契数)

线程版本:

class Fib extends CorRunThread<Integer, Integer> {

    @Override
    public Integer call() {
        Integer times = getReceiveValue();
        do {
            int a = 1, b = 1;
            for (int i = 0; times != null && i < times; i++) {
                int temp = a + b;
                a = b;
                b = temp;
            }
            // A pythonic "yield", i.e., it returns `a` to the caller and waits `times` value from the next caller
            times = yield(a);
        } while (! isEnded());

        setResultForOuter(Integer.MAX_VALUE);
        return getResultForOuter();
    }
}

class MainRun extends CorRunThread<String, String> {

    @Override
    public String call() {

        // The fib coroutine would be recycled by its parent
        // (no requirement to call its start() and stop() manually)
        // Otherwise, if you want to share its instance and start/stop it manually,
        // please start it before being called by yieldFrom() and stop it in the end.
        Fib fib = new Fib();
        String result = "";
        Integer current;
        int times = 10;
        for (int i = 0; i < times; i++) {

            // A pythonic "yield from", i.e., it calls fib with `i` parameter and waits for returned value as `current`
            current = yieldFrom(fib, i);

            if (fib.getError() != null) {
                throw new RuntimeException(fib.getError());
            }

            if (current == null) {
                continue;
            }

            if (i > 0) {
                result += ",";
            }
            result += current;

        }

        setResultForOuter(result);

        return result;
    }
}

同步(非线程)版本:

class Fib extends CorRunSync<Integer, Integer> {

    @Override
    public Integer call() {
        Integer times = getReceiveValue();

        int a = 1, b = 1;
        for (int i = 0; times != null && i < times; i++) {
            int temp = a + b;
            a = b;
            b = temp;
        }
        yield(a);

        return getResultForOuter();
    }
}

class MainRun extends CorRunSync<String, String> {

    @Override
    public String call() {

        CorRun<Integer, Integer> fib = null;
        try {
            fib = new Fib();
        } catch (Exception e) {
            e.printStackTrace();
        }

        String result = "";
        Integer current;
        int times = 10;
        for (int i = 0; i < times; i++) {

            current = yieldFrom(fib, i);

            if (fib.getError() != null) {
                throw new RuntimeException(fib.getError());
            }

            if (current == null) {
                continue;
            }

            if (i > 0) {
                result += ",";
            }
            result += current;
        }

        stop();
        setResultForOuter(result);

        if (Utils.isEmpty(result)) {
            throw new RuntimeException("Error");
        }

        return result;
    }
}

执行(两种版本均可):

// Run the entry coroutine
MainRun mainRun = new MainRun();
mainRun.start();

// Wait for mainRun ending for 5 seconds
long startTimestamp = System.currentTimeMillis();
while(!mainRun.isEnded()) {
    if (System.currentTimeMillis() - startTimestamp > TimeUnit.SECONDS.toMillis(5)) {
        throw new RuntimeException("Wait too much time");
    }
}
// The result should be "1,1,2,3,5,8,13,21,34,55"
System.out.println(mainRun.getResultForOuter());
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.