3

这是我的场景:

  1. 进程 A 产生子进程 B 并旋转线程以耗尽 B 的输出。
  2. 进程 B 生成守护进程 C 并排出其输出。
  3. 进程 B 完成,守护进程仍然存在。
  4. 进程 A 发现进程 B 通过 process.waitFor() 退出。但是,它一直在读取进程 B 的输入流。这是因为 B 已经启动了一个守护进程。输入流仅在进程 C 退出时接收 EOF。

这只发生在 Windows 上。我正在使用 ProcessBuilder。这是我想出的解决方案,我想听听您的反馈,因为没有我真正喜欢的解决方案:

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

我真的很感激任何提示!

4

1 回答 1

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() 方法中,我有以下内容,其中“管道”变量是我正在捕获子进程的 stdout/stderr 的 Writer 实例。'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 指示,即使在子进程终止之后也是如此,因此,上面所有的诡计都是为了防止过时的线程和阻塞。

于 2012-08-15T08:35:13.610 回答