如何消费一个过程' stdout作为一个流,没有阻塞?

时间:2017-07-03 14:02:46

标签: java processbuilder

在Java(或clojure)中,我想启动一个外部进程并将其stdout作为流使用。理想情况下,我想要使用这个过程'每次外部进程刷新它时输出流,但不确定如何实现,以及如何在不阻塞的情况下完成它。

继续使用Java ProcessPipeInputStream进行外壳处理(例如a Unix ProcessPipeInputStream),我发现继承的InputStream方法有点低级可以使用,而不是确保每次生产者端刷新时以及以非阻塞方式从流中消耗的非阻塞方式。

许多代码示例在无限循环中阻塞输出流,从而占用线程以进行侦听。我希望这种阻止行为可以完全避免。

底线:

是否有一种非阻塞方式可以在输入流上进行通知,每次生成端都会刷新?

1 个答案:

答案 0 :(得分:1)

您需要创建一个单独的Thread,它将从这样的流中消耗,允许您的程序的其余部分执行并行执行的任何操作。

class ProcessOutputReader implements Runnable {

   private InputStream processOutput;

   public ProcessOutputReader(final InputStream processOutput) {
       this.processOutput = processOutput;
   } 

   @Override
   public void run() {
      int nextByte;
      while ((nextByte = processOutput.read()) != -1) {
        // do whatever you need to do byte-by-byte.
        processByte(nextByte);
      }
   }
}

class Main {
   public static void main(final String[] args) {
       final Process proc = ...; 
       final ProcessOutputReader reader = new ProcessOutputReader(proc.getInputStream());
       final Thread processOutputReaderThread = new Thread(reader);
       processOutputReaderThread.setDaemon(true); // allow the VM to terminate if this is the only thread still active.
       processOutputReaderThread.start();
       ...
       // if you wanna wait for the whole process output to be processed at some point you can do this:
       try {
         processOutputReaderThread.join();
       } catch (final InterruptedException ex) {
         // you need to decide how to recover from if your wait was interrupted.
       }
   }
}

如果不是逐字节处理,而是希望将每个同花顺处理为一个单一的......我不确定100%保证能够抓住每个进程刷新。在所有过程中,自己的IO框架软件(Java,C,Python等)可以处理" flush"不同的操作,也许你最终收到的是该外部进程中任何给定刷新的多个字节块。

在任何情况下,您都可以尝试使用InputStream的{​​{1}}方法执行此操作:

available

我不是百分之百确定这一点,但我认为不能相信 @Override public void run() { int nextByte; while ((nextByte = processOutput.read()) != -1) { final int available = processOutput.available(); byte[] block = new byte[available + 1]; block[0] = nextByte; final int actuallyAvailable = processOutput.read(block, 1, available); if (actuallyAvailable < available) { if (actuallyAvailable == -1) { block = new byte[] { nextByte }; } else { block = Arrays.copyOf(block, actuallyAvailable + 1); } } // do whatever you need to do on that block now. processBlock(block); } } 会返回一个保证的下限,你可以在不被阻塞的情况下检索到的字节数,也不会下一个available如果需要,{1}}操作将返回该数量的可用字节;这就是上面的代码检查读取的实际字节数(read)的原因。