Scala - 在外部进程退出时获得回调

时间:2011-08-25 18:01:36

标签: scala

我正在寻找用scala替换我的很多perl。我倾向于做很多事情的一件事是我公司的其他团队提供给我的调用二进制文件(通常是编译的C ++,但可能是java,其他perl脚本,q脚本等)。

例如,要做一些复杂的数学运算,我会启动一个外部二进制文件,然后将输入管道输出。然后,我会听取其stdout流的结果,并stderr流用于诊断消息。在perl中,我使用POE::Wheel::Run小部件执行此操作。我在scala中提出了类似(并且更好)的东西,但我想让它更强大。它是ProcessIO对象的一个​​小包装器。它看起来像这样:

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
    }
}

然后我会这样使用它:

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

打印出来:

o: LOWER

这让我获得了90%,但是当进程退出时,最好是获得回调。它可能会退出,因为我已关闭输入流并且其内部循环停止,它可能会自行退出,或者它可能会退出,因为我已将其杀死。在回调中,最好知道它停止的原因和退出代码。

我对如何解决这个问题感到有点失落,任何帮助都会受到赞赏(对上述代码的任何编辑当然都是受欢迎的 - 我有点像菜鸟)。

我正在使用2.9.0.1

3 个答案:

答案 0 :(得分: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中使用它。

由JVM提供并由Process包装的scala.sys.Process类非常可行,很难不阻塞线程

答案 1 :(得分: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

谢谢大家!

答案 2 :(得分:2)

您是否考虑过生成一个新线程,然后调用阻塞方法process.exitValue()?然后,您可以拨打回叫。