2011-08-25 7 views
10

私は多くの私のperlをscalaに置き換えることを考えています。私がたくさんする傾向があるのは、私の会社の他のチームから私に提供されるバイナリ(通常はC++のコンパイルですが、java、他のperlスクリプト、qスクリプトなど)です。スカラ - 外部プロセスが終了したときにコールバックを取得する

たとえば、いくつかの複雑な数学を実行するには、私は外国のバイナリの1つを開始し、それに入力をパイプします。その後、stdoutストリームの結果と、stderrストリームの診断メッセージを聞きます。 perlでは、私はPOE::Wheel::Runウィジェットを使ってこれを行います。私はスカラーで類似した(そしてより良い)ものを思いついたが、もっと頑強にしたい。それはProcessIOオブジェクトの周りの小さなラッパーです。

o: LOWER 

をこれが私の90%を取得しますが、何が次のようになります。プリントアウトし

val exe = new Exe("tr [a-z] [A-Z]", 
        out => println("o: " + out), 
        err => println("e: " + err)) 
exe.write("lower") 
exe.close() 

:私は、このようにそれを使用したい

class Exe(command: String, out: String => Unit, err: String => Unit) { 

    import scala.sys.process._ 
    import scala.io._ 
    import java.io._ 
    import scala.concurrent._ 

    val inputStream = new SyncVar[OutputStream]; 

    val process = Process(command).run(
     new ProcessIO(
      stdin => inputStream.put(stdin), 
      stdout => Source.fromInputStream(stdout).getLines.foreach(out), 
      stderr => Source.fromInputStream(stderr).getLines.foreach(err))); 

    def write(s: String): Unit = synchronized { 
     inputStream.get.write((s + "\n").getBytes) 
    } 

    def close(): Unit = { 
     inputStream.get.close 
    } 
} 

:それはこのようになりますniceは、プロセスが終了したときにコールバックを取得することです。入力ストリームを閉じて内部ループが停止したり、単独で終了したり、終了したりする可能性があるため、終了することがあります。コールバックでは、なぜ停止したのか、終了コードを知っておくと良いでしょう。

私はこれについてどうやって行けばいいのか分かりませんが、どんな助けもありがたいです(そして上記のコードの編集はもちろん歓迎です - 私はちょっとしたことです) 。私は

+2

個人的には、 'Process'には何らかの' isFinished'ポーリングメソッドがありません。それは私が変更したいことの1つですが、didierdが提供するソリューションはあなたが望むもののように見えます。 –

答えて

10

exitValueを呼び出すプロセスの終了を待つことができます。コールバックが発生する別のスレッドで行うことができます。たぶんクラスProcessは、このように魅惑することができます:あなたが好きなように

import scala.concurrent.ops.spawn 
implicit def ProcessWithCallback(p: Process) { 
    def whenTerminatedDo(callback: Int => Unit) = spawn{ 
    val exitValue = p.exitValue; callback(p) 
    } 
} 

あなたはその後、Exeでそれを使うことができます。

scala.sys.ProcessによってJVMによって与えられ、包まれProcessクラスは本当にかなりfeableあり、スレッドをブロックしないように難しいでしょう

+2

別のスレッドがブロックされたり、ポーリングされたりすることなく、コールバックを得る方法はありません( 'process'がサポートされているかどうかにかかわらず)。それは、ポーリングがうまくいったと言いました。@Daniel。 –

+0

JVMプロセスに関する私の問題は、このようなメソッドを提供していないため(時間切れの待ち時間でさえも)、スレッドを使うよりもうまくやることができないということです。 Process APIが(Javaで)より大きい場合、ここで説明したようなメソッドを提供すると、JVMはOS実装の機能をいくつかの実装で使用し、必要に応じてスレッドの処理を行うことができます。私はシステムプログラミングに精通していませんが、UNIXではSIGCHLDを覚えています。待ちスレッドは必要ありませんでした。私は何かが恋しいですか? –

+0

SIGCHLDは割り込みです。 JVMモデルには中断はありません。さて、[このリンク](http://www.ibm.com/developerworks/java/library/i-signalhandling/)には、非標準的な方法があります。とにかく、問題は標準的な割り込みメカニズムがないことです。 –

2

2.9.0.1を使用してい

を使用すると、その後、ブロッキング方法process.exitValue()を呼び出します新しいスレッドを産卵と考えたことがありますか?コールバックを呼び出すことができます。

3

更新されたバージョンの新しいスレッドを作成するために、spawnを使用して、その終了コードのためのブロックを待ち

class Exe(command:String, out:String=>Unit, err:String=>Unit, onExit:Int=>Unit) { 

    import scala.sys.process._ 
    import scala.io._ 
    import java.io._ 
    import scala.concurrent._ 
    import scala.concurrent.ops.spawn 

    val inputStream = new SyncVar[OutputStream]; 

    val process = Process(command).run(
     new ProcessIO(
      stdin => inputStream.put(stdin), 
      stdout => Source.fromInputStream(stdout).getLines.foreach(out), 
      stderr => Source.fromInputStream(stderr).getLines.foreach(err))); 

    spawn { onExit(process.exitValue()) } 

    def write(s:String):Unit = synchronized { 
     inputStream.get.write((s + "\n").getBytes) 
    } 

    def close():Unit = { 
     inputStream.get.close 
    } 
} 

この

import java.util.concurrent.CountDownLatch 

val latch = new CountDownLatch(1) 

val exe = new Exe("tr [a-z] [A-Z]", 
     out => println("o: " + out), 
     err => println("e: " + err), 
     code=> {println(code) ; latch.countDown() }) 
exe.write("lower") 
exe.close() 

latch.await 

印刷物のように使用することができます

o: LOWER 
0 

ありがとうございました!

関連する問題