java Process' inputStream卡住了

时间:2012-01-26 00:50:41

标签: java process daemon processbuilder child-process

这是我的情景:

  1. 进程A生成子进程B并旋转线程以消耗B的输出。
  2. 进程B生成守护进程C并排出其输出。
  3. 进程B结束,守护进程仍然存在。
  4. 进程A发现进程B通过process.waitFor()退出。然而,它仍然在阅读进程B的输入流。这是因为B已经启动了一个守护进程。仅当进程C退出时,输入流才会收到EOF。
  5. 这只发生在Windows上。我正在使用ProcessBuilder。以下是我提出的解决方案,我希望听到您的反馈,因为我不喜欢这些解决方案:

    1. 我可以使用jna来生成守护进程C.这样我就可以创建一个“足够分离”的进程,并且进程A不会停留在从B中排出流。它可以工作,但我不是很热衷于该解决方案,因为它意味着一些本机代码(以及很多,因为我热衷于消耗输入)。一些灵感如何通过JNA来实现:http://yajsw.sourceforge.net(但它包含的方式不仅仅是流程的开始)。
    2. 在jre7上运行。 Jdk7为ProcessBuilder带来了一些新的好东西,例如inheritIO()也解决了我的问题。显然,当inheritIO()打开时,我可以简单地关闭守护进程C中的所有流(无论如何我都这样做,因为它是守护进程)并且解决了这个问题。但是,我需要在jre5 +
    3. 上运行
    4. 在生成守护进程C之前关闭进程B 中的System.out和System.err。再次,它解决了问题,但我确实需要这些流在进程B中工作,因为我写了有用的东西给他们。不好。我希望通过在B& B之间放置一些自举过程来利用这一特性。 C但这并没有解决问题。
    5. 我在linux上没有这个问题所以我只能在linux上运行吗?不,我不能。
    6. 使过程A以非阻塞方式排出过程B的输出。这有点奏效,但不方便。例如。 inputStream.read()不可中断。我可以使用inputStream.available()但它不区分EOF和零字节可用。因此,如果过程A从不对B的输出EOF感兴趣,那么解决方案才有用。此外,这个解决方案似乎更加CPU密集,而且通常......感觉很尴尬而且不是真正的防弹。
    7. 在--dry-run模式下运行进程C,只检查它是否可以启动。它尝试启动,发送欢迎消息并退出。它不再长时间运行,因此不会阻止读取。进程B可以获得足够的信心,可以启动C,并且我们可以使用相对简单的JNA代码来生成分离的进程而不消耗其输出(消耗输出会使JNA相关的代码变得混乱和重量级)。唯一的问题是我们不再是消费者进程的C输出,但它可以通过使C写入进程B可以使用的众所周知的文件来解决。这个解决方案更像是一个大而丑陋的解决方案,但对我们来说是可行的。无论如何,我们正在尝试解决方案1)。
    8. 我真的很感激任何提示!

1 个答案:

答案 0 :(得分:0)

我刚遇到同样的问题。我想我有一个问题的解决方法。在进程A中,我在Process.waitFor()之后有以下代码片段,其中outT和errT分别是读取进程B的stdout和stderr的线程:

try {
  outT.join(1000);
  if (outT.isAlive()) {
    errmsg("stdout reader still alive, interrupting", null);
    outT.interrupt();
  }
} catch (Exception e) {
  errmsg("Exception caught from out stream reader: "+e, e);
}
try {
  errT.join(1000);
  if (errT.isAlive()) {
    errmsg("stderr reader still alive, interrupting", null);
    errT.interrupt();
  }
} catch (Exception e) {
  errmsg("Exception caught from err stream reader: "+e, e);
}
p.destroy();

不确定是否需要p.destroy(),但我一直在尝试各种组合来解决问题。

无论如何,在outT / errT线程的run()方法中,我有以下内容,其中'pipe'变量是一个Writer实例,我正在捕获子进程的stdout / stderr。 'in'变量是从Process:

获得的stdout或stderr流
try {
  r = new BufferedReader(new InputStreamReader(in, enc));
  String line;
  while (true) {
    if (Thread.currentThread().isInterrupted()) {
      errmsg("Text stream reader interrupted", null);
      break;
    }
    if (r.ready()) {
      line = r.readLine();
      if (line == null) {
        break;
      }
      pipe.write(line);
      pipe.write(SystemUtil.EOL);
      if (autoFlush) {
        pipe.flush();
      }
    }
  }
  pipe.flush();

} catch (Throwable t) {
  errmsg("Exception caught: "+t, t);
  try { pipe.flush(); } catch (Exception noop) {}

} finally {
  IOUtil.closeQuietly(in);
  IOUtil.closeQuietly(r);
}

似乎我从未从任何子流程获得EOF指示,即使在子流程终止之后,因此,上面的所有诡计都会阻止过时的线程和阻塞。