2011-08-25 93 views
10

我正在用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

+2

就我個人而言,我認爲糟糕的是'Process'沒有某種'isFinished'輪詢方法。這是我會改變的一件事,儘管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只要你喜歡。

由JVM給包裹由scala.sys.ProcessProcess類實在是相當feable,這將是很難不阻塞線程

+2

如果沒有單獨的線程阻塞或輪詢(無論提供哪個「進程」支持),都無法獲得回調。這就是說,民意調查會很好。 –

+0

@Daniel。我對JVM進程的問題是,因爲它沒有提供這樣的方法(甚至沒有定時等待!),我們不能比使用線程做得更好。如果Process API比較大(用java),提供一種方法,比如這裏討論的方法,那麼JVM就可以在某些實現中利用操作系統特有的特性,如果需要在其他實現中就可以自己完成這些線程。我不熟悉系統編程,但是我記得UNIX下的SIGCHLD,沒有需要等待的線程 - 在那些日子裏也不是真的可用;-)。我想念什麼? –

+0

SIGCHLD是一箇中斷。 JVM模型沒有中斷。那麼,[這個鏈接](http://www.ibm.com/developerworks/java/library/i-signalhandling/)表明這是一種非標準的方式。無論如何,問題在於缺乏標準的中斷機制。 –

2

你有沒有考慮產生一個新的線程,然後將調用阻塞方法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 

謝謝大家!