81

この質問は、 Java の既存のコルーチン実装に関する私の質問に関連しています。私が思うに、Java で現在利用可能なコルーチンの完全な実装がないことが判明した場合、それらを実装するには何が必要になるでしょうか?

その質問で述べたように、私は次のことを知っています。

  1. 舞台裏でスレッド/スレッドプールとして「コルーチン」を実装できます。
  2. コルーチンを可能にするために、バックグラウンドで JVM バイトコードを使用してトリッキーなことを行うことができます。
  3. いわゆる「Da Vinci Machine」JVM 実装には、バイトコード操作なしでコルーチンを実行可能にするプリミティブがあります。
  4. コルーチンに対するさまざまな JNI ベースのアプローチも可能です。

それぞれの欠点を順番に説明します。

スレッドベースのコルーチン

この「解決策」は病的です。コルーチンの要点は、スレッド化、ロック、カーネル スケジューリングなどのオーバーヘッドを回避することです。コルーチンは軽量で高速であり、ユーザー空間でのみ実行されるはずです。厳しい制限のあるフルティルト スレッドの観点からそれらを実装すると、すべての利点が取り除かれます。

JVM バイトコード操作

このソリューションは、実行するのが少し難しいですが、より実用的です。これは、C でコルーチン ライブラリのアセンブリ言語に飛び込むのとほぼ同じです (これは、多くのコルーチン ライブラリが機能します)。心配して適切に処理する必要があるアーキテクチャは 1 つだけであるという利点があります。

また、非準拠のスタックで同じことを行う方法を見つけられない限り、完全に準拠した JVM スタック (つまり、Android がないことを意味します) でのみコードを実行する必要があります。ただし、これを行う方法を見つけたとしても、システムの複雑さとテストの必要性は 2 倍になります。

ダ・ヴィンチ・マシン

Da Vinci Machine は実験用としては優れていますが、標準の JVM ではないため、その機能をどこでも利用できるわけではありません。実際、ほとんどのプロダクション環境では、ダ ヴィンチ マシンの使用が明確に禁止されているのではないかと思います。したがって、これを使用してクールな実験を行うことはできますが、現実の世界にリリースする予定のコードには使用できません。

これには、上記の JVM バイトコード操作ソリューションと同様の問題も追加されています。代替スタック (Android など) では機能しません。

JNI の実装

このソリューションは、Java でこれを行うポイントをまったく意味のないものにします。CPU とオペレーティング システムの組み合わせごとに独立したテストが必要であり、それぞれが苛立たしい微妙な障害のポイントになる可能性があります。もちろん、別の方法として、1 つのプラットフォームに完全に縛り付けることもできますが、これもまた、Java で何かを行うという意味を完全に無意味にします。

そう...

これらの 4 つの手法のいずれかを使用せずに Java でコルーチンを実装する方法はありますか? それとも、これら 4 つのうち最も臭いの少ないもの (JVM 操作) を代わりに使用することを余儀なくされますか?


追加するために編集:

混乱が含まれないようにするために、これは私の他の質問に関連する質問ですが、同じではありません。不必要に車輪の再発明を避けるために、既存の実装を探しています。これは、Java でコルーチンを実装する方法に関する質問です。その意図は、さまざまな質問をさまざまなスレッドに保持することです。

4

8 に答える 8

44

私はこれを見てみます: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;
}

likeのローカル変数を抽象化する必要がis_doneあります。また、 likeの操作ではスタックを使用しない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();
    }
}

他の基本的な制御フロー構造についても、同様のトリックを実行できます。理想的には、これらのヘルパークラスのライブラリを構築し、それらを使用してこれらの単純なインターフェイスを実装し、最終的にコルーチンのセマンティクスを提供します。私がここに書いたものはすべて、一般化して大幅に拡張できると確信しています。

于 2010-05-17T06:02:12.823 に答える
0

Javaで使用するコルーチンクラスがあります。これはスレッドに基づいており、スレッドを使用すると並列操作が可能になるという利点があります。これは、マルチコアマシンでは利点となる可能性があります。したがって、スレッドベースのアプローチを検討することをお勧めします。

于 2012-09-04T10:33:05.063 に答える