lethevert is a programmer このページをアンテナに追加 RSSフィード

21/6/2007 (Thu) 晴れ

Java : Generator

PythonのGeneratorみたいなものをJavaで作ってみようと思った。Javaには本物のマルチスレッドがあるのだから、Generatorくらいは当然作れるのだけれど、いざちゃんと協調動作させようとすると上手く書けなくてあきらめる人とか多いのじゃないかとか思ったので。

実装上は2つのスレッドを協調動作させているのだけれど、使っている側からは1つのスレッドが交互に制御を移しているような感覚で使えます。

これがサンプルコード。

public class GenMain{
  public static void main (String[] args)
  {
    Generator<String> g = new Generator<String>(new Call());
    String s = "開始";
    i = 0;
    do{
      System.err.println(i);
      System.err.println(s);
      i = 0;
    }while (null != (s = g.call()));
    System.err.println(i);
  }

  private static int i;
  private static class Call implements Generator.Call<String>{
    public String call (Generator.Cont<String> c){
      i += 2;
      c.yield("最初");
      i += 3;
      c.yield("2つ目");
      i += 4;
      c.yield("最後");
      i += 5;
      return null;
    }
  }
}

出力はこんな感じ。

$ java GenMain
0
開始
2
最初
3
2つ目
4
最後
5

Generatorのソースは次のとおり。最後まで実行しないと内部スレッドが回収されないので、shutdown()メソッドとか追加して、使い終わった後に内部スレッドの破棄を行えるようにしておくべきかと思う。

public class Generator<A>{
  public interface Call<A>{
    A call (Cont<A> c);
  }
  public interface Cont<A>{
    void yield (A o);
  }

  public Generator (final Call<A> c){
    this.mutex = new Object();
    this.t = new Thread (){
      public void run (){
        yield_to(true);
        ret = c.call(new ContImpl());
        synchronized (mutex){
          is_main = true;
          mutex.notify();
          mutex = null;
        }
      }
    };
    synchronized (mutex){
      t.start();
      yield_to(false);
    }
  }

  private final Thread t;
  private volatile A ret = null;
  private volatile Object mutex;
  private volatile boolean is_main = true;

  private void yield_to (boolean b){
    if (null == mutex) throw new IllegalStateException();
    synchronized (mutex){
      is_main = b;
      mutex.notify();
      while (b == is_main)
        try{ mutex.wait(100);}
        catch(InterruptedException e){}
    }
  }

  public boolean finished (){
    return mutex == null;
  }

  public A call (){
    if (null == mutex) throw new IllegalStateException();
    synchronized (mutex){
      if (!is_main) throw new IllegalStateException();
    }
    yield_to(false);
    return ret;
  }

  private class ContImpl implements Cont<A>{
    public void yield (A o){
      if (null == mutex) throw new IllegalStateException();
      synchronized (mutex){
        if (is_main || (!Thread.currentThread().equals(t)))
          throw new IllegalStateException();
      }
      ret = o;
      yield_to(true);
    }
  }
}

これだとこれだと 2007/06/23 19:46 ジェネレータの中でジェネレータを呼び出して、そのジェネレータの中で別のジェネレータを呼び出したりする場合の同期制御がものすごく大変じゃない?

lethevertlethevert 2007/06/23 22:14 やってみた?

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/lethevert/20070621/p2