2011-08-25 21 views
10

Tôi đang tìm cách thay thế rất nhiều perl của mình bằng scala. Một trong những điều tôi có xu hướng làm rất nhiều là gọi các tệp nhị phân (thường được biên dịch C++, nhưng có thể là java, các kịch bản perl khác, tập lệnh q, v.v) do các nhóm khác tại công ty của tôi cung cấp cho tôi.Scala - nhận được cuộc gọi lại khi quá trình bên ngoài thoát

Ví dụ, để thực hiện một số phép toán phức tạp, tôi sẽ bắt đầu một trong các tệp nhị phân nước ngoài, sau đó nhập đầu vào của tôi vào nó. Sau đó tôi sẽ nghe luồng stdout của mình để có kết quả và luồng stderr cho các thông báo chẩn đoán. Trong perl, tôi sẽ làm điều này bằng cách sử dụng một widget POE::Wheel::Run. Tôi đã nghĩ ra điều gì đó tương tự (và đẹp hơn nhiều) trong scala, nhưng tôi muốn làm cho nó trở nên mạnh mẽ hơn. Đó là một wrapper nhỏ xung quanh một đối tượng ProcessIO. Nó trông giống như thế này:

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

Sau đó tôi muốn sử dụng nó như thế này:

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

nào in ra:

o: LOWER 

này được tôi 90% đó, nhưng điều gì sẽ tốt đẹp sẽ là để có được một cuộc gọi lại khi quá trình thoát. Nó có thể thoát vì tôi đã đóng luồng đầu vào và vòng lặp nội bộ của nó dừng lại, nó có thể tự thoát ra, hoặc nó có thể thoát ra vì tôi đã giết nó. Trong cuộc gọi lại, nó sẽ là tốt để biết lý do tại sao nó dừng lại, và mã thoát.

Tôi có chút mất mát về cách thực hiện việc này, bất kỳ trợ giúp nào cũng sẽ được đánh giá cao (và mọi chỉnh sửa đối với mã ở trên đều được chào đón - tôi là một chút noob) .

Tôi đang sử dụng 2.9.0.1

+2

Cá nhân, tôi nghĩ rằng nó hút rằng 'Process' không có một số loại phương pháp bỏ phiếu' isFinished'. Đó là một điều tôi thay đổi, mặc dù giải pháp được cung cấp bởi didierd có vẻ giống như những gì bạn muốn. –

Trả lời

10

Bạn có thể đợi đến khi kết thúc quá trình gọi exitValue. Bạn có thể làm điều đó trong một chủ đề riêng biệt, trong đó gọi lại sẽ xảy ra. Có lẽ lớp Process thể pimped như sau:

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

Sau đó, bạn có thể sử dụng trong Exe như bạn muốn.

Lớp Process được đưa ra bởi JVM và bọc bởi scala.sys.Process thực sự là khá feable, nó sẽ là khó khăn không phải để ngăn chặn một sợi

+2

Không có cách nào để nhận được cuộc gọi lại mà không có một chuỗi riêng biệt bị chặn hoặc bỏ phiếu (không có vấn đề gì 'quy trình hỗ trợ' được cung cấp). Điều đó nói rằng, bỏ phiếu sẽ được tốt đẹp. –

+0

@Daniel. Vấn đề của tôi với quá trình JVM là bởi vì nó không cung cấp một phương thức như vậy (thậm chí không phải là một thời gian chờ đợi!), Chúng ta không thể làm tốt hơn việc sử dụng một Thread. Nếu API quá trình lớn hơn (trong java), cung cấp một phương thức như được thảo luận ở đây, JVM có thể sử dụng các tính năng dành riêng cho hệ điều hành trong một số triển khai và tự làm chủ đề nếu cần. Tôi không quen thuộc với lập trình hệ thống, nhưng tôi nhớ SIGCHLD dưới UNIX, không cần phải chờ đợi chủ đề - cũng không thực sự có sẵn trong những ngày ;-). Tôi có nhớ điều gì không? –

+0

SIGCHLD bị gián đoạn. Không có sự gián đoạn trong mô hình JVM. Vâng, [liên kết này] (http://www.ibm.com/developerworks/java/library/i-signalhandling/) cho thấy có một cách không chuẩn về nó. Dù sao, vấn đề là thiếu một cơ chế ngắt tiêu chuẩn. –

2

Bạn đã cân nhắc tạo ra một chuỗi mới mà sau đó sẽ gọi phương thức chặn process.exitValue()? Sau đó, bạn có thể gọi lại cuộc gọi lại.

3

phiên bản cập nhật sử dụng spawn để tạo ra một chủ đề mới mà khối và đợi cho mã lối ra

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

có thể sử dụng như thế này

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 

in

o: LOWER 
0 

cảm ơn tất cả mọi người!

Các vấn đề liên quan