[解決済み] Javaでコルーチンを実装する
質問
この質問は、私の質問と関連しています。 Javaにおける既存のコルーチンの実装 . もし、私が思うに、現在Javaにコルーチンの完全な実装がないことが判明した場合、それを実装するためには何が必要でしょうか?
その質問で申し上げたように、私は以下のことを知っています。
- コルーチン("coroutine")を裏側のスレッド/スレッドプールとして実装することができます。
- コルーチンを可能にするために、裏でJVMバイトコードでトリック的なことをすることができます。
- いわゆるダ・ヴィンチ・マシンと呼ばれるJVM実装は、コルーチンを無人で実行するためのプリミティブを持っています。 バイトコード操作
- また、JNIベースの様々なコルーチンへのアプローチも可能です。
それぞれの欠点を順を追って説明します。
スレッドベースのコルーチン
この解決策は病的です。 コルーチンの要点は 避ける スレッド、ロック、カーネルスケジューリングなどのオーバーヘッドが発生します。 コルーチンは軽量で高速であり、ユーザ空間のみで実行されることが前提となっています。 コルーチンを、厳しい制約のある本格的なスレッドとして実装すると、その利点がすべて失われてしまいます。
JVMバイトコード操作
この解決策は、少し難しいですが、より実用的です。 これは、C言語のコルーチンライブラリのためにアセンブリ言語に飛び降りるのとほぼ同じです(多くのコルーチンライブラリはこの方法で動作しています)が、心配するのは1つのアーキテクチャだけで、正しく動作させることができるという利点があります。
また、非準拠のスタックで同じことをする方法が見つからない限り、完全に準拠したJVMスタック(つまり、たとえばAndroidはダメ)でのみコードを実行するように縛られることになります。 しかし、これを実現する方法を見つけたとしても、システムの複雑さとテストの必要性が倍増してしまいます。
ダ・ヴィンチ・マシン
ダ・ヴィンチ・マシンは実験用としてはクールですが、標準的なJVMではないので、その機能はどこでも使えるというわけではありません。 実際、ほとんどの生産環境では、ダ・ヴィンチ・マシンの使用は特に禁止されていると思います。 ですから、私はこのマシンを使ってクールな実験をすることはできますが、実世界にリリースすることを想定したコードには使えません。
また、上記のJVMバイトコード操作のソリューションと同様の問題があります。
JNIの実装
この解決策は、Javaでこれを行う意味を全く無くしてしまいます。 CPUとOSの組み合わせごとに独立したテストが必要で、それぞれ微妙に失敗してイライラする可能性がある点です。 あるいは、もちろん、完全に1つのプラットフォームに自分を縛ることもできますが、これも、Javaで物事を行う意味を全く無意味なものにしてしまいます。
それで...
この4つのテクニックのどれかを使わずに、Javaでコルーチンを実装する方法はないのでしょうか? それとも、この4つのうち最も臭いの少ないもの(JVM操作)を代わりに使わざるを得ないのでしょうか?
追加で編集しました。
混乱を収束させるために、これは 関連 に対する質問 私のもう一つの が、同じではありません。 その1つは 既存の を実装し、不必要に車輪の再発明をしないようにするためです。 この質問は、他の質問に答えられない場合、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
(program-counter) の代わりにヒープに現在の操作の状態を保持することです。 そのため、たくさんのヘルパークラスが必要になります。
例えば
理想的な世界では、次のようなコンシューマを書きたいとしましょう(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();
}
}
同様のトリックは、他の基本的な制御フロー構造についても可能です。 理想的には、これらのヘルパークラスのライブラリを構築し、それらを使って、最終的に共同ルーチンのセマンティクスを与えるこれらの単純なインタフェースを実装することでしょう。 ここに書いたことはすべて一般化でき、大きく拡張できるはずです。
関連
-
[解決済み] 要素 'beans' の宣言が見つかりません。
-
[解決済み] javaでAnnotation Inheritanceのようなものはあるのでしょうか?
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] JavaでNullPointerExceptionを回避する方法
-
[解決済み] JavaにおけるHashMapとHashtableの違いは何ですか?
-
[解決済み] Java Mapの各エントリを効率的に反復処理するには?
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] JavaでArrayListではなくLinkedListを使用するのはいつですか?
-
[解決済み] JavaでStringをintに変換するにはどうしたらいいですか?
-
[解決済み】Javaの「ダブルブレース初期化」の効率化?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] この配列の中の数字を入れ替えるには、何が足りないのでしょうか?ジャバ
-
[解決済み] Java の substring() の時間複雑性
-
[解決済み] パラメータ[変数]の不正な修飾子;finalのみが許可される[closed]。
-
[解決済み] プロトコルハンドラの初期化に失敗しました。
-
[解決済み] Androidのコールバックとは何ですか?重複
-
[解決済み] javac ソースファイルが見つかりません
-
[解決済み] 要素 'beans' の宣言が見つかりません。
-
[解決済み] 1行目2列目でBEGIN_ARRAYを期待したが、BEGIN_OBJECTだった。
-
[解決済み] Eclipseでクラスとそれに対応するファイルの名前を変更する方法は?
-
[解決済み] 文字列の巻き方