2009-03-18 43 views
15

Tôi muốn khởi chạy một quá trình từ Java, đọc đầu ra của nó và lấy mã trả về của nó. Nhưng trong khi nó thực hiện, tôi muốn có thể hủy bỏ nó. Tôi bắt đầu bằng cách khởi chạy quá trình:Chạy chương trình bên ngoài từ Java, đọc đầu ra, cho phép gián đoạn

ProcessBuilder pb = new ProcessBuilder(args); 
pb.redirectErrorStream(true); 
Process proc = pb.start(); 

Nếu tôi gọi proc.waitFor(), tôi không thể làm gì cho đến khi quá trình thoát. Vì vậy, tôi giả sử tôi cần một cái gì đó như thế này:

while (true) { 
    see if process has exited 
    capture some output from the process 
    decide if I want to cancel it, and if so, cancel it 
    sleep for a while 
} 

Điều này có đúng không? Ai đó có thể cho tôi một ví dụ về làm thế nào để làm điều này trong Java?

Trả lời

11

Dưới đây là một ví dụ về những gì tôi nghĩ rằng bạn muốn làm:

ProcessBuilder pb = new ProcessBuilder(args); 
pb.redirectErrorStream(true); 
Process proc = pb.start(); 

InputStream is = proc.getInputStream(); 
InputStreamReader isr = new InputStreamReader(is); 
BufferedReader br = new BufferedReader(isr); 

String line; 
int exit = -1; 

while ((line = br.readLine()) != null) { 
    // Outputs your process execution 
    System.out.println(line); 
    try { 
     exit = proc.exitValue(); 
     if (exit == 0) { 
      // Process finished 
     } 
    } catch (IllegalThreadStateException t) { 
     // The process has not yet finished. 
     // Should we stop it? 
     if (processMustStop()) 
      // processMustStop can return true 
      // after time out, for example. 
      proc.destroy(); 
    } 
} 

Bạn có thể cải thiện nó :-) Tôi không có một môi trường thực tế để kiểm tra nó bây giờ, nhưng bạn có thể tìm thấy một số thông tin here.

+2

Điều gì xảy ra nếu không có đầu ra trong một thời gian?Sẽ không treo trên br.readLine() cho đến khi có một dòng đã sẵn sàng để đọc? Tôi không chắc có bao nhiêu hoặc ít đầu ra quá trình của tôi sẽ gửi. –

+2

Go point. Bạn có thể tạo một chuỗi phân tách để phân tích nếu quá trình đó phải dừng lại. Nó có thể chạy song song với vòng lặp while và phá hủy quá trình nếu nó treo trong một thời gian (hoặc bất kỳ điều kiện nào khác). –

+0

(i có nghĩa là "điểm tốt") –

5

Một lớp helper như thế này sẽ làm các trick:

public class ProcessWatcher implements Runnable { 

    private Process p; 
    private volatile boolean finished = false; 

    public ProcessWatcher(Process p) { 
     this.p = p; 
     new Thread(this).start(); 
    } 

    public boolean isFinished() { 
     return finished; 
    } 

    public void run() { 
     try { 
      p.waitFor(); 
     } catch (Exception e) {} 
     finished = true; 
    } 

} 

Sau đó, bạn sẽ thực hiện vòng lặp của bạn chính xác như bạn mô tả:

Process p = Runtime.getRuntime().exec("whatever command"); 
ProcessWatcher pw = new ProcessWatcher(p); 
InputStream output = p.getInputStream(); 
while(!pw.isFinished()) { 
    processOutput(output); 
    if(shouldCancel()) p.destroy(); 
    Thread.sleep(500); 
} 

Tùy theo điều kiện nào sẽ khiến bạn muốn để tiêu diệt các quá trình, bạn có thể muốn làm điều đó trong một chủ đề riêng biệt. Nếu không, bạn có thể chặn trong khi chờ đợi cho đầu ra chương trình nhiều hơn để xử lý và không bao giờ thực sự có được tùy chọn để tiêu diệt nó.

EDIT: McDowell là 100% ngay trong nhận xét của mình bên dưới, vì vậy tôi đã biến biến hoàn thành dễ bay hơi.

+1

Biến "hoàn thành" ít nhất phải được khai báo là "biến động". – McDowell

+0

Đó là mối quan tâm của tôi; nếu không có đầu ra trong một thời gian thì sao? Nếu tôi gọi một cái gì đó như BufferedReader.readLine(), tôi không muốn nó treo mãi mãi cho đến khi có đầu ra; Tôi muốn có cơ hội để hủy bỏ. –

+0

Bạn có thể tạo Runnable mới với: khoảng trống công khai() { khi (! Pw.isFinished()) { if (shouldCancel()) p.destroy(); Thread.sleep (500); } } rồi đến Chủ đề mới (myrunnable) .start(). –

0

Điều gì sẽ khiến bạn quyết định giết quá trình - sự kiện không đồng bộ (như đầu vào từ người dùng) hoặc sự kiện đồng bộ (ví dụ: quy trình đã thực hiện những gì bạn muốn làm)? Tôi đoán đó là trước đây - đầu vào từ người dùng khiến bạn quyết định hủy quá trình con.

Ngoài ra, bạn mong đợi bao nhiêu đầu ra để xử lý con? Nếu nó là rất nhiều, sau đó các subprocess có thể chặn nếu bạn không đọc từ dòng sản lượng của nó một cách nhanh chóng đủ.

Tình huống của bạn có thể thay đổi, nhưng có vẻ như bạn sẽ cần ít nhất hai luồng khác nhau - một để quyết định có hủy quy trình hay không và xử lý đầu ra từ tiến trình con.

Có một cái nhìn vào đây để xem chi tiết chút: http://java.sun.com/developer/JDCTechTips/2005/tt0727.html#2

+0

Tôi đã chạy trong một chuỗi và ai đó bên ngoài chuỗi đó sẽ đặt một trong các trường của tôi khi đến lúc hủy. –

8

tôi khuyên bạn nên kiểm tra ra Apache Commons Exec để tránh tái tạo lại bánh xe. Nó có một số tính năng tốt đẹp như lựa chọn giữa thực thi đồng bộ so với không đồng bộ, cũng như một giải pháp tiêu chuẩn để sinh ra một quy trình cơ quan giám sát có thể giúp trong thời gian thực hiện trong trường hợp bị kẹt.

+0

Tôi đã chiến đấu vì tôi muốn có một chương trình "thuần khiết" chỉ sử dụng JDK cốt lõi. Nhưng bạn nói đúng, cách này là tốt hơn và giải quyết rất nhiều vấn đề tôi không biết tôi đã đăng ký khi tôi cố gắng để tạo của riêng tôi. – GojiraDeMonstah

2

Làm thế nào về vấn đề này (xem cách nó hoạt động trong jcabi-heroku-maven-plugin):

/** 
* Wait for the process to stop, logging its output in parallel. 
* @param process The process to wait for 
* @return Stdout produced by the process 
* @throws InterruptedException If interrupted in between 
*/ 
private String waitFor(final Process process) throws InterruptedException { 
    final BufferedReader reader = new BufferedReader(
     new InputStreamReader(process.getInputStream()) 
    ); 
    final CountDownLatch done = new CountDownLatch(1); 
    final StringBuffer stdout = new StringBuffer(); 
    new Thread(
     new VerboseRunnable(
      new Callable<Void>() { 
       @Override 
       public Void call() throws Exception { 
        while (true) { 
         final String line = reader.readLine(); 
         if (line == null) { 
          break; 
         } 
         System.out.println(">> " + line); 
         stdout.append(line); 
        } 
        done.countDown(); 
        return null; 
       } 
      }, 
      false 
     ) 
    ).start(); 
    try { 
     process.waitFor(); 
    } finally { 
     done.await(); 
     IOUtils.closeQuietly(reader); 
    } 
    return stdout.toString(); 
} 

ps. Bây giờ triển khai này có sẵn dưới dạng com.jcabi.log.VerboseProcess lớp từ jcabi-log tạo phẩm.

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